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);
+    }
+
 }