Add fast pair upload footprint entry
Test: pair with device test account key is generated and api call to try
upload info
Bug: 214495869
Change-Id: Ic0a77c1794dabfde505662328f4df31aa34446de
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java
index b57e6ad..c98848d 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java
@@ -71,6 +71,8 @@
import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection.ChangeObserver;
import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothAdapter;
import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.BluetoothOperationTimeoutException;
+import com.android.server.nearby.common.locator.Locator;
+import com.android.server.nearby.fastpair.FastPairController;
import com.android.server.nearby.intdefs.FastPairEventIntDefs.BrEdrHandoverErrorCode;
import com.android.server.nearby.intdefs.FastPairEventIntDefs.ConnectErrorCode;
import com.android.server.nearby.intdefs.FastPairEventIntDefs.CreateBondErrorCode;
@@ -1325,6 +1327,7 @@
throws InterruptedException, ExecutionException, TimeoutException,
NoSuchAlgorithmException,
BluetoothException {
+ Locator.get(mContext, FastPairController.class).setShouldUpload(false);
if (!shouldWriteAccountKey()) {
// For FastPair 2.0, here should be a subsequent pairing case.
return null;
@@ -1347,7 +1350,9 @@
if (!mPreferences.getIsRetroactivePairing()) {
try (ScopedTiming scopedTiming2 = new ScopedTiming(mTimingLogger,
"Start CloudSyncing")) {
- mContext.startService(createCloudSyncingIntent(accountKey));
+ // Start to sync to the footprint
+ Locator.get(mContext, FastPairController.class).setShouldUpload(true);
+ //mContext.startService(createCloudSyncingIntent(accountKey));
} catch (SecurityException e) {
Log.w(TAG, "Error adding device.", e);
}
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
index a792c0b..6166052 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
@@ -39,6 +39,14 @@
Context mContext;
String mBleAddress;
+ /** The types about how the bloomfilter is processed. */
+ public enum ProcessBloomFilterType {
+ IGNORE, // The bloomfilter is not handled. e.g. distance is too far away.
+ CACHE, // The bloomfilter is recognized in the local cache.
+ FOOTPRINT, // Need to check the bloomfilter from the footprints.
+ ACCOUNT_KEY_HIT // The specified account key was hit the bloom filter.
+ }
+
/**
* Constructor function.
*/
@@ -68,6 +76,9 @@
Cache.ScanFastPairStoreItem.newBuilder().setAddress(mBleAddress)
.setAntiSpoofingPublicKey(publicKey)
.build());
+ } else {
+ // Start to process bloomfilter
+
}
}
}
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairController.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairController.java
index f39849c..6efaae7 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairController.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairController.java
@@ -21,7 +21,9 @@
import static com.android.server.nearby.fastpair.FastPairManager.EXTRA_NOTIFICATION_ID;
import static com.google.common.io.BaseEncoding.base16;
+import static com.google.common.primitives.Bytes.concat;
+import android.accounts.Account;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
@@ -31,19 +33,26 @@
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
+import com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress;
import com.android.server.nearby.common.eventloop.Annotations;
import com.android.server.nearby.common.eventloop.EventLoop;
import com.android.server.nearby.common.eventloop.NamedRunnable;
import com.android.server.nearby.common.locator.Locator;
import com.android.server.nearby.fastpair.cache.DiscoveryItem;
import com.android.server.nearby.fastpair.cache.FastPairCacheManager;
+import com.android.server.nearby.fastpair.footprint.FastPairUploadInfo;
import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager;
import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager;
import com.android.server.nearby.fastpair.notification.FastPairNotificationManager;
import com.android.server.nearby.fastpair.pairinghandler.PairingProgressHandlerBase;
+import com.android.server.nearby.provider.FastPairDataProvider;
+import com.google.common.hash.Hashing;
+import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -59,7 +68,11 @@
private final FastPairCacheManager mFastPairCacheManager;
private final FootprintsDeviceManager mFootprintsDeviceManager;
private boolean mIsFastPairing = false;
- @Nullable private Callback mCallback;
+ // boolean flag whether upload to footprint or not.
+ private boolean mShouldUpload = false;
+ @Nullable
+ private Callback mCallback;
+
public FastPairController(Context context) {
mContext = context;
mEventLoop = Locator.get(mContext, EventLoop.class);
@@ -208,6 +221,84 @@
return companionApp == null ? null : companionApp.trim();
}
+ /**
+ * Function to handle when scanner find bloomfilter.
+ */
+ @Annotations.EventThread
+ public FastPairAdvHandler.ProcessBloomFilterType onBloomFilterDetect(FastPairAdvHandler handler,
+ boolean advertiseInRange) {
+ if (mIsFastPairing) {
+ return FastPairAdvHandler.ProcessBloomFilterType.IGNORE;
+ }
+ // Check if the device is in the cache or footprint.
+ return FastPairAdvHandler.ProcessBloomFilterType.CACHE;
+ }
+
+ /**
+ * Add newly paired device info to footprint
+ */
+ @WorkerThread
+ public void addDeviceToFootprint(String publicAddress, byte[] accountKey,
+ DiscoveryItem discoveryItem) {
+ if (!mShouldUpload) {
+ return;
+ }
+ Log.d("FastPairController", "upload device to footprint");
+ FastPairManager.processBackgroundTask(() -> {
+ Cache.StoredDiscoveryItem storedDiscoveryItem =
+ prepareStoredDiscoveryItemForFootprints(discoveryItem);
+ if (storedDiscoveryItem != null) {
+ byte[] hashValue =
+ Hashing.sha256()
+ .hashBytes(
+ concat(accountKey, BluetoothAddress.decode(publicAddress)))
+ .asBytes();
+ FastPairUploadInfo uploadInfo =
+ new FastPairUploadInfo(storedDiscoveryItem, ByteString.copyFrom(accountKey),
+ ByteString.copyFrom(hashValue));
+ // account data place holder here
+ FastPairDataProvider.getInstance().optIn(new Account("empty", "empty"));
+ FastPairDataProvider.getInstance().upload(
+ new Account("empty", "empty"), uploadInfo);
+
+ }
+
+ });
+ }
+
+ @Nullable
+ private Cache.StoredDiscoveryItem getStoredDiscoveryItemFromAddressForFootprints(
+ String bleAddress) {
+
+ List<DiscoveryItem> discoveryItems = new ArrayList<>();
+ //cacheManager.getAllDiscoveryItems();
+ for (DiscoveryItem discoveryItem : discoveryItems) {
+ if (bleAddress.equals(discoveryItem.getMacAddress())) {
+ return prepareStoredDiscoveryItemForFootprints(discoveryItem);
+ }
+ }
+ return null;
+ }
+
+ static Cache.StoredDiscoveryItem prepareStoredDiscoveryItemForFootprints(
+ DiscoveryItem discoveryItem) {
+ Cache.StoredDiscoveryItem.Builder storedDiscoveryItem =
+ discoveryItem.getCopyOfStoredItem().toBuilder();
+ // Strip the mac address so we aren't storing it in the cloud and ensure the item always
+ // starts as enabled and in a good state.
+ storedDiscoveryItem.clearMacAddress();
+
+ return storedDiscoveryItem.build();
+ }
+
+ /**
+ * FastPairConnection will check whether write account key result if the account key is
+ * generated change the parameter.
+ */
+ public void setShouldUpload(boolean shouldUpload) {
+ mShouldUpload = shouldUpload;
+ }
+
private final NamedRunnable mReEnableAllDeviceItemsRunnable =
new NamedRunnable("reEnableAllDeviceItems") {
@Override
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
index bc1603e..7e2151d 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
@@ -29,6 +29,7 @@
import android.nearby.NearbyDevice;
import android.nearby.NearbyManager;
import android.nearby.ScanCallback;
+import android.nearby.ScanRequest;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -50,6 +51,7 @@
import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager;
import com.android.server.nearby.fastpair.pairinghandler.PairingProgressHandlerBase;
import com.android.server.nearby.util.FastPairDecoder;
+import com.android.server.nearby.util.ForegroundThread;
import com.android.server.nearby.util.Hex;
import java.security.GeneralSecurityException;
@@ -80,6 +82,7 @@
final LocatorContextWrapper mLocatorContextWrapper;
final IntentFilter mIntentFilter;
final Locator mLocator;
+ private boolean mAllowScan = false;
private final BroadcastReceiver mScreenBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -91,12 +94,13 @@
Log.d("FastPairService", " the nearby manager is " + nearbyManager);
if (nearbyManager != null) {
- // Uncomment this if you want to get mainline half sheet
-// nearbyManager.startScan(
-// new ScanRequest.Builder()
-// .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR).build(),
-// ForegroundThread.getExecutor(),
-// mScanCallback);
+ if (mAllowScan) {
+ nearbyManager.startScan(
+ new ScanRequest.Builder()
+ .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR).build(),
+ ForegroundThread.getExecutor(),
+ mScanCallback);
+ }
} else {
Log.d("FastPairService", " the nearby manager is null");
}
@@ -256,6 +260,13 @@
connection.pair(
accountKey != null ? accountKey
: item.getAuthenticationPublicKeySecp256R1());
+ if (accountKey == null) {
+ // Account key is null so it is initial pairing
+ if (sharedSecret != null) {
+ Locator.get(context, FastPairController.class).addDeviceToFootprint(
+ connection.getPublicAddress(), sharedSecret.getKey(), item);
+ }
+ }
byte[] key = pairingProgressHandlerBase.getKeyForLocalCache(accountKey,
connection, sharedSecret);
@@ -265,6 +276,7 @@
// CacheManager to save the content here
}
} else {
+ // Fast Pair one
connection.pair();
}
pairingProgressHandlerBase.onPairingSuccess(connection.getPublicAddress());
@@ -308,6 +320,14 @@
}
/**
+ * Processed task in a background thread
+ */
+ @Annotations.EventThread
+ public static void processBackgroundTask(Runnable runnable) {
+ getExecutor().execute(runnable);
+ }
+
+ /**
* This function should only be called on main thread since there is no lock
*/
private static Executor getExecutor() {
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairCacheManager.java b/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairCacheManager.java
index 1b35913..fb2adb2 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairCacheManager.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairCacheManager.java
@@ -27,6 +27,9 @@
import com.google.protobuf.InvalidProtocolBufferException;
+import java.util.ArrayList;
+import java.util.List;
+
import service.proto.Cache;
import service.proto.Rpcs;
@@ -133,6 +136,40 @@
}
/**
+ * Get all of the discovery item related info in the cache.
+ */
+ public List<Cache.StoredDiscoveryItem> getAllSavedStoreDiscoveryItem() {
+ List<Cache.StoredDiscoveryItem> storedDiscoveryItemList = new ArrayList<>();
+ SQLiteDatabase db = mDiscoveryItemDbHelper.getReadableDatabase();
+ String[] projection = {
+ DiscoveryItemContract.DiscoveryItemEntry.COLUMN_MODEL_ID,
+ DiscoveryItemContract.DiscoveryItemEntry.COLUMN_SCAN_BYTE
+ };
+ Cursor cursor = db.query(
+ DiscoveryItemContract.DiscoveryItemEntry.TABLE_NAME,
+ projection,
+ null,
+ null,
+ null,
+ null,
+ null
+ );
+
+ while (cursor.moveToNext()) {
+ byte[] res = cursor.getBlob(cursor.getColumnIndexOrThrow(
+ DiscoveryItemContract.DiscoveryItemEntry.COLUMN_SCAN_BYTE));
+ try {
+ Cache.StoredDiscoveryItem item = Cache.StoredDiscoveryItem.parseFrom(res);
+ storedDiscoveryItemList.add(item);
+ } catch (InvalidProtocolBufferException e) {
+ Log.e("FastPairCacheManager", "storediscovery has error");
+ }
+
+ }
+ cursor.close();
+ return storedDiscoveryItemList;
+ }
+ /**
* Get scan result from local database use model id
*/
public Cache.StoredScanResult getStoredScanResult(String modelId) {
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/footprint/FastPairUploadInfo.java b/nearby/service/java/com/android/server/nearby/fastpair/footprint/FastPairUploadInfo.java
new file mode 100644
index 0000000..6c9aff0
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/fastpair/footprint/FastPairUploadInfo.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.fastpair.footprint;
+
+
+import com.google.protobuf.ByteString;
+
+import service.proto.Cache;
+
+/**
+ * Wrapper class that upload the pair info to the footprint.
+ */
+public class FastPairUploadInfo {
+
+ private Cache.StoredDiscoveryItem mStoredDiscoveryItem;
+
+ private ByteString mAccountKey;
+
+ private ByteString mSha256AccountKeyPublicAddress;
+
+
+ public FastPairUploadInfo(Cache.StoredDiscoveryItem storedDiscoveryItem, ByteString accountKey,
+ ByteString sha256AccountKeyPublicAddress) {
+ mStoredDiscoveryItem = storedDiscoveryItem;
+ mAccountKey = accountKey;
+ mSha256AccountKeyPublicAddress = sha256AccountKeyPublicAddress;
+ }
+
+ public Cache.StoredDiscoveryItem getStoredDiscoveryItem() {
+ return mStoredDiscoveryItem;
+ }
+
+ public ByteString getAccountKey() {
+ return mAccountKey;
+ }
+
+
+ public ByteString getSha256AccountKeyPublicAddress() {
+ return mSha256AccountKeyPublicAddress;
+ }
+}
diff --git a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
index 2ecbaa3..5c57ddf 100644
--- a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
@@ -16,6 +16,7 @@
package com.android.server.nearby.provider;
+import android.accounts.Account;
import android.annotation.Nullable;
import android.content.Context;
import android.nearby.FastPairDataProviderBase;
@@ -23,6 +24,8 @@
import androidx.annotation.WorkerThread;
+import com.android.server.nearby.fastpair.footprint.FastPairUploadInfo;
+
import service.proto.Rpcs;
/** FastPairDataProvider is a singleton that implements APIs to get FastPair data. */
@@ -72,4 +75,19 @@
}
throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed");
}
+
+ /**
+ * opt in default account to fast pair
+ */
+ public void optIn(Account account) {
+
+ }
+
+ /**
+ * Upload the device to the footprint
+ */
+ public void upload(Account account, FastPairUploadInfo uploadInfo) {
+
+ }
+
}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/FastPairCacheManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/FastPairCacheManagerTest.java
index adbb12a..ff98ecf 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/FastPairCacheManagerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/FastPairCacheManagerTest.java
@@ -34,12 +34,17 @@
public class FastPairCacheManagerTest {
private static final String MODEL_ID = "001";
+ private static final String MODEL_ID2 = "002";
private static final String APP_NAME = "APP_NAME";
@Mock
DiscoveryItem mDiscoveryItem;
+ @Mock DiscoveryItem mDiscoveryItem2;
Cache.StoredDiscoveryItem mStoredDiscoveryItem = Cache.StoredDiscoveryItem.newBuilder()
.setTriggerId(MODEL_ID)
.setAppName(APP_NAME).build();
+ Cache.StoredDiscoveryItem mStoredDiscoveryItem2 = Cache.StoredDiscoveryItem.newBuilder()
+ .setTriggerId(MODEL_ID2)
+ .setAppName(APP_NAME).build();
@Before
public void setup() {
@@ -69,4 +74,23 @@
assertThat(fastPairCacheManager.getStoredDiscoveryItem(MODEL_ID).getAppName())
.isEqualTo(APP_NAME);
}
+
+ @Test
+ public void getAllInfo() {
+ Context mContext = ApplicationProvider.getApplicationContext();
+ when(mDiscoveryItem.getCopyOfStoredItem()).thenReturn(mStoredDiscoveryItem);
+ when(mDiscoveryItem.getTriggerId()).thenReturn(MODEL_ID);
+ when(mDiscoveryItem2.getCopyOfStoredItem()).thenReturn(mStoredDiscoveryItem2);
+ when(mDiscoveryItem2.getTriggerId()).thenReturn(MODEL_ID2);
+
+ FastPairCacheManager fastPairCacheManager = new FastPairCacheManager(mContext);
+ fastPairCacheManager.saveDiscoveryItem(mDiscoveryItem);
+
+ assertThat(fastPairCacheManager.getAllSavedStoreDiscoveryItem()).hasSize(2);
+
+ fastPairCacheManager.saveDiscoveryItem(mDiscoveryItem2);
+
+ assertThat(fastPairCacheManager.getAllSavedStoreDiscoveryItem()).hasSize(3);
+ }
+
}