[DO NOT MERGE] Fast Pair Half Sheet server side implementation

Refactor doc: go/fp-halfsheet-aosp

Bug: 196262706
Bug: 196644352
Bug: 218771606
Test: Built band verified flow manually
Change-Id: I86d81cd582482889946185a86399ebca21c84e56
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/Constant.java b/nearby/service/java/com/android/server/nearby/fastpair/Constant.java
index 5958007..0695b5f 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/Constant.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/Constant.java
@@ -33,22 +33,11 @@
 
     public static final String EXTRA_BINDER = "com.android.server.nearby.fastpair.BINDER";
     public static final String EXTRA_BUNDLE = "com.android.server.nearby.fastpair.BUNDLE_EXTRA";
-    public static final String SUCCESS_STATE = "SUCCESS";
-    public static final String FAIL_STATE = "FAIL";
-    public static final String DISMISS = "DISMISS";
-    public static final String NEED_CONFIRM_PASSKEY = "NEED CONFIRM PASSKEY";
-    // device support assistant additional setup
-    public static final String NEED_ADDITIONAL_SETUP = "NEED ADDITIONAL SETUP";
-    public static final String SHOW_PAIRING_WITHOUT_INTERACTION =
-            "SHOW_PAIRING_WITHOUT_INTERACTION";
     public static final String ACTION_FAST_PAIR_HALF_SHEET_CANCEL =
             "com.android.nearby.ACTION_FAST_PAIR_HALF_SHEET_CANCEL";
-    public static final String ACTION_FAST_PAIR_HALF_SHEET_BAN_STATE_RESET =
-            "com.android.nearby.ACTION_FAST_PAIR_BAN_STATE_RESET";
     public static final String EXTRA_HALF_SHEET_INFO =
             "com.android.nearby.halfsheet.HALF_SHEET";
     public static final String EXTRA_HALF_SHEET_TYPE =
             "com.android.nearby.halfsheet.HALF_SHEET_TYPE";
     public static final String DEVICE_PAIRING_FRAGMENT_TYPE = "DEVICE_PAIRING";
-
 }
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 793e126..1264ade 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairController.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairController.java
@@ -16,21 +16,15 @@
 
 package com.android.server.nearby.fastpair;
 
-import static com.android.server.nearby.common.bluetooth.fastpair.BroadcastConstants.EXTRA_RETROACTIVE_PAIR;
-import static com.android.server.nearby.common.fastpair.service.UserActionHandlerBase.EXTRA_COMPANION_APP;
-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;
+import android.nearby.FastPairDevice;
 import android.text.TextUtils;
 import android.util.Log;
 
-import androidx.annotation.UiThread;
 import androidx.annotation.WorkerThread;
 
 import com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress;
@@ -110,73 +104,60 @@
     /**
      * Pairing function.
      */
-    @UiThread
-    public void pair(Intent intent) {
-        String itemId = intent.getStringExtra(UserActionHandler.EXTRA_ITEM_ID);
-        int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
-        byte[] discoveryItem = intent.getByteArrayExtra(UserActionHandler.EXTRA_DISCOVERY_ITEM);
-        String accountKeyString = intent.getStringExtra(UserActionHandler.EXTRA_FAST_PAIR_SECRET);
-        String companionApp = trimCompanionApp(intent.getStringExtra(EXTRA_COMPANION_APP));
-        byte[] accountKey = accountKeyString != null ? base16().decode(accountKeyString) : null;
-        boolean isRetroactivePair = intent.getBooleanExtra(EXTRA_RETROACTIVE_PAIR, false);
+    public void pair(FastPairDevice fastPairDevice) {
+        byte[] discoveryItem = fastPairDevice.getData();
+        String modelId = fastPairDevice.getModelId();
 
+        Log.v(TAG, "pair: fastPairDevice " + fastPairDevice);
         mEventLoop.postRunnable(
-                new NamedRunnable("fastPairWith=" + itemId) {
+                new NamedRunnable("fastPairWith=" + modelId) {
                     @Override
                     public void run() {
-                        DiscoveryItem item = null;
-                        if (discoveryItem != null) {
-                            try {
-                                item = new DiscoveryItem(mContext,
-                                        Cache.StoredDiscoveryItem.parseFrom(discoveryItem));
-                            } catch (InvalidProtocolBufferException e) {
-                                Log.w(TAG,
-                                        "Error parsing serialized discovery item with size "
-                                                + discoveryItem.length);
+                        try {
+                            DiscoveryItem item = new DiscoveryItem(mContext,
+                                    Cache.StoredDiscoveryItem.parseFrom(discoveryItem));
+                            if (TextUtils.isEmpty(item.getMacAddress())) {
+                                Log.w(TAG, "There is no mac address in the DiscoveryItem,"
+                                        + " ignore pairing");
                                 return;
                             }
+                            // Check enabled state to prevent multiple pair attempts if we get the
+                            // intent more than once (this can happen due to an Android platform
+                            // bug - b/31459521).
+                            if (item.getState()
+                                    != Cache.StoredDiscoveryItem.State.STATE_ENABLED) {
+                                Log.d(TAG, "Incorrect state, ignore pairing");
+                                return;
+                            }
+                            boolean useLargeNotifications =
+                                    item.getAuthenticationPublicKeySecp256R1() != null;
+                            FastPairNotificationManager fastPairNotificationManager =
+                                    new FastPairNotificationManager(mContext, item,
+                                            useLargeNotifications);
+                            FastPairHalfSheetManager fastPairHalfSheetManager =
+                                    Locator.get(mContext, FastPairHalfSheetManager.class);
+                            mFastPairCacheManager.saveDiscoveryItem(item);
+
+                            PairingProgressHandlerBase pairingProgressHandlerBase =
+                                    PairingProgressHandlerBase.create(
+                                            mContext,
+                                            item,
+                                            /* companionApp= */ null,
+                                            /* accountKey= */ null,
+                                            mFootprintsDeviceManager,
+                                            fastPairNotificationManager,
+                                            fastPairHalfSheetManager,
+                                            /* isRetroactivePair= */ false);
+
+                            pair(item,
+                                    /* accountKey= */ null,
+                                    /* companionApp= */ null,
+                                    pairingProgressHandlerBase);
+                        } catch (InvalidProtocolBufferException e) {
+                            Log.w(TAG,
+                                    "Error parsing serialized discovery item with size "
+                                            + discoveryItem.length);
                         }
-
-
-                        if (item == null || TextUtils.isEmpty(item.getMacAddress())) {
-                            Log.w(TAG, "Invalid DiscoveryItem, ignore pairing");
-                            return;
-                        }
-
-                        // Check enabled state to prevent multiple pair attempts if we get the
-                        // intent more than once (this can happen due to an Android platform
-                        // bug - b/31459521).
-                        if (item.getState() != Cache.StoredDiscoveryItem.State.STATE_ENABLED
-                                && !isRetroactivePair) {
-                            Log.d(TAG, "Incorrect state, ignore pairing");
-                            return;
-                        }
-
-                        boolean useLargeNotifications = accountKey != null
-                                || item.getAuthenticationPublicKeySecp256R1() != null;
-                        FastPairNotificationManager fastPairNotificationManager =
-                                notificationId == -1
-                                        ? new FastPairNotificationManager(mContext, item,
-                                        useLargeNotifications)
-                                        : new FastPairNotificationManager(mContext, item,
-                                                useLargeNotifications, notificationId);
-                        FastPairHalfSheetManager fastPairHalfSheetManager =
-                                Locator.get(mContext, FastPairHalfSheetManager.class);
-
-                        mFastPairCacheManager.saveDiscoveryItem(item);
-
-                        PairingProgressHandlerBase pairingProgressHandlerBase =
-                                PairingProgressHandlerBase.create(
-                                        mContext,
-                                        item,
-                                        companionApp,
-                                        accountKey,
-                                        mFootprintsDeviceManager,
-                                        fastPairNotificationManager,
-                                        fastPairHalfSheetManager,
-                                        isRetroactivePair);
-
-                        pair(item, accountKey, companionApp, pairingProgressHandlerBase);
                     }
                 });
     }
@@ -315,4 +296,4 @@
     interface Callback {
         void fastPairUpdateDeviceItemsEnabled(boolean enabled);
     }
-}
+}
\ No newline at end of file
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 9e1a718..3a3c962 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
@@ -56,6 +56,7 @@
 import com.android.server.nearby.fastpair.cache.DiscoveryItem;
 import com.android.server.nearby.fastpair.cache.FastPairCacheManager;
 import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager;
+import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager;
 import com.android.server.nearby.fastpair.pairinghandler.PairingProgressHandlerBase;
 import com.android.server.nearby.util.FastPairDecoder;
 import com.android.server.nearby.util.ForegroundThread;
@@ -194,7 +195,7 @@
             @Nullable byte[] accountKey,
             FootprintsDeviceManager footprints,
             PairingProgressHandlerBase pairingProgressHandlerBase) {
-
+        FastPairHalfSheetManager manager = Locator.get(context, FastPairHalfSheetManager.class);
         try {
             pairingProgressHandlerBase.onPairingStarted();
             if (pairingProgressHandlerBase.skipWaitingScreenUnlock()) {
@@ -279,6 +280,10 @@
                 // Fast Pair one
                 connection.pair();
             }
+
+            // TODO(b/213373051): Merge logic with pairingProgressHandlerBase or delete the
+            // pairingProgressHandlerBase class.
+            manager.showPairingSuccessHalfSheet(connection.getPublicAddress());
             pairingProgressHandlerBase.onPairingSuccess(connection.getPublicAddress());
         } catch (BluetoothException
                 | InterruptedException
@@ -287,7 +292,11 @@
                 | ExecutionException
                 | PairingException
                 | GeneralSecurityException e) {
-            Log.e(TAG, "FastPair: Error");
+            Log.e(TAG, "Failed to pair.", e);
+
+            // TODO(b/213373051): Merge logic with pairingProgressHandlerBase or delete the
+            // pairingProgressHandlerBase class.
+            manager.showPairingFailed();
             pairingProgressHandlerBase.onPairingFailed(e);
         }
     }
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManager.java b/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManager.java
index 42839b2..6f79e6e 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManager.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManager.java
@@ -29,10 +29,14 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.nearby.FastPairDevice;
+import android.nearby.FastPairStatusCallback;
+import android.nearby.PairStatusMetadata;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.nearby.common.locator.LocatorContextWrapper;
 import com.android.server.nearby.fastpair.FastPairController;
 import com.android.server.nearby.fastpair.cache.DiscoveryItem;
@@ -47,27 +51,24 @@
  * Fast Pair ux manager for half sheet.
  */
 public class FastPairHalfSheetManager {
-    static final String ACTIVITY_INTENT_ACTION = "android.nearby.SHOW_HALFSHEET";
+    private static final String ACTIVITY_INTENT_ACTION = "android.nearby.SHOW_HALFSHEET";
     private static final String HALF_SHEET_CLASS_NAME =
             "com.android.nearby.halfsheet.HalfSheetActivity";
+    private static final String TAG = "FPHalfSheetManager";
 
     private String mHalfSheetApkPkgName;
-    private Context mContext;
-    private LocatorContextWrapper mLocatorContextWrapper;
+    private final LocatorContextWrapper mLocatorContextWrapper;
 
-    /**
-     * Construct function
-     */
+    FastPairService mFastPairService;
+
     public FastPairHalfSheetManager(Context context) {
-        mContext = context;
-        mLocatorContextWrapper = new LocatorContextWrapper(context);
+        this(new LocatorContextWrapper(context));
     }
 
-    /**
-     * Construct function for test
-     */
-    public FastPairHalfSheetManager(LocatorContextWrapper locatorContextWrapper) {
+    @VisibleForTesting
+    FastPairHalfSheetManager(LocatorContextWrapper locatorContextWrapper) {
         mLocatorContextWrapper = locatorContextWrapper;
+        mFastPairService = new FastPairService();
     }
 
     /**
@@ -79,14 +80,13 @@
             if (mLocatorContextWrapper != null) {
                 String packageName = getHalfSheetApkPkgName();
                 if (packageName == null) {
-                    Log.e("FastPairHalfSheetManager", "package name is null");
+                    Log.e(TAG, "package name is null");
                     return;
                 }
-                HalfSheetCallback callback = new HalfSheetCallback();
-                callback.setmFastPairController(
+                mFastPairService.setFastPairController(
                         mLocatorContextWrapper.getLocator().get(FastPairController.class));
                 Bundle bundle = new Bundle();
-                bundle.putBinder(EXTRA_BINDER, callback);
+                bundle.putBinder(EXTRA_BINDER, mFastPairService);
                 mLocatorContextWrapper
                         .startActivityAsUser(new Intent(ACTIVITY_INTENT_ACTION)
                                         .putExtra(EXTRA_HALF_SHEET_INFO,
@@ -97,11 +97,9 @@
                                         .setComponent(new ComponentName(packageName,
                                                 HALF_SHEET_CLASS_NAME)),
                                 UserHandle.CURRENT);
-
             }
         } catch (IllegalStateException e) {
-            Log.e("FastPairHalfSheetManager",
-                    "Can't resolve package that contains half sheet");
+            Log.e(TAG, "Can't resolve package that contains half sheet");
         }
     }
 
@@ -109,7 +107,15 @@
      * Shows pairing fail half sheet.
      */
     public void showPairingFailed() {
-        Log.d("FastPairHalfSheetManager", "show fail half sheet");
+        FastPairStatusCallback pairStatusCallback = mFastPairService.getPairStatusCallback();
+        if (pairStatusCallback != null) {
+            Log.v(TAG, "showPairingFailed: pairStatusCallback not NULL");
+            pairStatusCallback.onPairUpdate(new FastPairDevice.Builder().build(),
+                    new PairStatusMetadata(PairStatusMetadata.Status.FAIL));
+        } else {
+            Log.w(TAG, "FastPairHalfSheetManager failed to show success half sheet because "
+                    + "the pairStatusCallback is null");
+        }
     }
 
     /**
@@ -129,14 +135,22 @@
      * This function will handle pairing steps for half sheet.
      */
     public void showPairingHalfSheet(DiscoveryItem item) {
-        Log.d("FastPairHalfSheetManager", "show pairing half sheet");
+        Log.d(TAG, "show pairing half sheet");
     }
 
     /**
      * Shows pairing success info.
      */
     public void showPairingSuccessHalfSheet(String address) {
-        Log.d("FastPairHalfSheetManager", "show success half sheet");
+        FastPairStatusCallback pairStatusCallback = mFastPairService.getPairStatusCallback();
+        if (pairStatusCallback != null) {
+            pairStatusCallback.onPairUpdate(
+                    new FastPairDevice.Builder().setBluetoothAddress(address).build(),
+                    new PairStatusMetadata(PairStatusMetadata.Status.SUCCESS));
+        } else {
+            Log.w(TAG, "FastPairHalfSheetManager failed to show success half sheet because "
+                    + "the pairStatusCallback is null");
+        }
     }
 
     /**
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairService.java b/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairService.java
index 53c2c12..8c0d572 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairService.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairService.java
@@ -73,7 +73,13 @@
      * Asks the Fast Pair service to pair the device.
      */
     @Override
-    public void connect(FastPairDevice fastPairDevice) {}
+    public void connect(FastPairDevice fastPairDevice) {
+        if (mFastPairController != null) {
+            mFastPairController.pair(fastPairDevice);
+        } else {
+            Log.w(TAG, "Failed to connect because there is no FastPairController.");
+        }
+    }
 
     public FastPairStatusCallback getPairStatusCallback() {
         return mFastPairStatusCallback;
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/HalfSheetCallback.java b/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/HalfSheetCallback.java
deleted file mode 100644
index 2c792ed..0000000
--- a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/HalfSheetCallback.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2021 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.halfsheet;
-
-import android.content.Intent;
-import android.nearby.IFastPairHalfSheetCallback;
-import android.util.Log;
-
-import com.android.server.nearby.fastpair.FastPairController;
-
-
-/**
- * Callback to send ux action back to nearby service.
- */
-public class HalfSheetCallback extends IFastPairHalfSheetCallback.Stub {
-    private FastPairController mFastPairController;
-
-    public HalfSheetCallback() {
-    }
-
-    /**
-     * Set function for Fast Pair controller.
-     */
-    public void setmFastPairController(FastPairController fastPairController) {
-        mFastPairController = fastPairController;
-    }
-
-    /**
-     * Half Sheet connection button clicked.
-     */
-    @Override
-    public void onHalfSheetConnectionConfirm(Intent intent) {
-        Log.d("FastPairHalfSheet", "Call back receiver");
-        if (mFastPairController != null) {
-            mFastPairController.pair(intent);
-        }
-    }
-}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManagerTest.java
index ab18f54..58e4c47 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManagerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManagerTest.java
@@ -16,11 +16,6 @@
 
 package com.android.server.nearby.fastpair.halfsheet;
 
-
-import static com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager.ACTIVITY_INTENT_ACTION;
-
-import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -72,7 +67,6 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
-
         mScanFastPairStoreItem = Cache.ScanFastPairStoreItem.newBuilder()
                 .setAddress(BLEADDRESS)
                 .setDeviceName(NAME)
@@ -85,7 +79,6 @@
         ResolveInfo resolveInfo = new ResolveInfo();
         List<ResolveInfo> resolveInfoList = new ArrayList<>();
 
-
         mPackageManager = mock(PackageManager.class);
         when(mContextWrapper.getPackageManager()).thenReturn(mPackageManager);
         resolveInfo.activityInfo = new ActivityInfo();
@@ -108,8 +101,6 @@
 
         verify(mContextWrapper, atLeastOnce())
                 .startActivityAsUser(intentArgumentCaptor.capture(), eq(UserHandle.CURRENT));
-        Intent launchIntent = intentArgumentCaptor.getValue();
-        assertThat(launchIntent.getAction()).isEqualTo(ACTIVITY_INTENT_ACTION);
     }
 
     @Test
@@ -118,7 +109,6 @@
         ResolveInfo resolveInfo = new ResolveInfo();
         List<ResolveInfo> resolveInfoList = new ArrayList<>();
 
-
         mPackageManager = mock(PackageManager.class);
         when(mContextWrapper.getPackageManager()).thenReturn(mPackageManager);
         resolveInfo.activityInfo = new ActivityInfo();
@@ -142,6 +132,5 @@
 
         verify(mContextWrapper, never())
                 .startActivityAsUser(intentArgumentCaptor.capture(), eq(UserHandle.CURRENT));
-
     }
 }