Merge "Add overlayble.xml to enable RRO" into tm-dev
diff --git a/nearby/framework/java/android/nearby/FastPairDataProviderBase.java b/nearby/framework/java/android/nearby/FastPairDataProviderBase.java
index f7af7b8..57467b7 100644
--- a/nearby/framework/java/android/nearby/FastPairDataProviderBase.java
+++ b/nearby/framework/java/android/nearby/FastPairDataProviderBase.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.nearby.aidl.ByteArrayParcel;
 import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel;
 import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel;
 import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataRequestParcel;
@@ -40,7 +41,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
 /**
  * Base class for fast pair providers outside the system server.
@@ -223,7 +226,18 @@
     }
 
     /**
-     * Class for reading FastPairAccountDevicesMetadataRequest.
+     * Class for reading FastPairAccountDevicesMetadataRequest, which specifies the Fast Pair
+     * account and the allow list of the FastPair device keys saved to the account (i.e., FastPair
+     * accountKeys).
+     *
+     * A Fast Pair accountKey is created when a Fast Pair device is saved to an account. It is per
+     * Fast Pair device per account.
+     *
+     * To retrieve all Fast Pair accountKeys saved to an account, the caller needs to set
+     * account with an empty allow list.
+     *
+     * To retrieve metadata of a selected list of Fast Pair devices saved to an account, the caller
+     * needs to set account with a non-empty allow list.
      */
     public static class FastPairAccountDevicesMetadataRequest {
 
@@ -233,10 +247,34 @@
                 final FastPairAccountDevicesMetadataRequestParcel metaDataRequestParcel) {
             this.mMetadataRequestParcel = metaDataRequestParcel;
         }
-        /** Get account. */
+
+        /**
+         * Get FastPair account, whose Fast Pair devices' metadata is requested.
+         *
+         * @return a FastPair account.
+         */
         public @NonNull Account getAccount() {
             return this.mMetadataRequestParcel.account;
         }
+
+        /**
+         * Get allowlist of Fast Pair devices using a collection of accountKeys.
+         * Note that as a special case, empty list actually means all FastPair devices under the
+         * account instead of none.
+         *
+         * @return allowlist of Fast Pair devices using a collection of accountKeys.
+         */
+        public @NonNull Collection<byte[]> getAccountKeys()  {
+            if (this.mMetadataRequestParcel.accountKeys == null) {
+                return new ArrayList<byte[]>(0);
+            }
+            List<byte[]> accountKeys =
+                    new ArrayList<>(this.mMetadataRequestParcel.accountKeys.length);
+            for (ByteArrayParcel accountKey : this.mMetadataRequestParcel.accountKeys) {
+                accountKeys.add(accountKey.byteArray);
+            }
+            return accountKeys;
+        }
     }
 
     /** Class for reading FastPairEligibleAccountsRequest. */
diff --git a/nearby/framework/java/android/nearby/aidl/ByteArrayParcel.aidl b/nearby/framework/java/android/nearby/aidl/ByteArrayParcel.aidl
new file mode 100644
index 0000000..53c73bd
--- /dev/null
+++ b/nearby/framework/java/android/nearby/aidl/ByteArrayParcel.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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 android.nearby.aidl;
+
+/**
+ * This is to support 2D byte arrays.
+ * {@hide}
+ */
+parcelable ByteArrayParcel {
+    byte[] byteArray;
+}
\ No newline at end of file
diff --git a/nearby/framework/java/android/nearby/aidl/FastPairAccountDevicesMetadataRequestParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairAccountDevicesMetadataRequestParcel.aidl
index 2cff632..5953935 100644
--- a/nearby/framework/java/android/nearby/aidl/FastPairAccountDevicesMetadataRequestParcel.aidl
+++ b/nearby/framework/java/android/nearby/aidl/FastPairAccountDevicesMetadataRequestParcel.aidl
@@ -17,6 +17,7 @@
 package android.nearby.aidl;
 
 import android.accounts.Account;
+import android.nearby.aidl.ByteArrayParcel;
 
 /**
  * Request details for Metadata of Fast Pair devices associated with an account.
@@ -24,4 +25,5 @@
  */
 parcelable FastPairAccountDevicesMetadataRequestParcel {
     Account account;
+    ByteArrayParcel[] accountKeys;
 }
\ No newline at end of file
diff --git a/nearby/halfsheet/res/drawable/fast_pair_ic_info.xml b/nearby/halfsheet/res/drawable/fast_pair_ic_info.xml
index cc514e5..7d61d1c 100644
--- a/nearby/halfsheet/res/drawable/fast_pair_ic_info.xml
+++ b/nearby/halfsheet/res/drawable/fast_pair_ic_info.xml
@@ -17,7 +17,8 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+    android:viewportHeight="24.0"
+    android:tint="@color/fast_pair_half_sheet_subtitle_color">
     <path
         android:fillColor="@color/fast_pair_half_sheet_subtitle_color"
         android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
diff --git a/nearby/halfsheet/res/layout/fast_pair_device_pairing_fragment.xml b/nearby/halfsheet/res/layout/fast_pair_device_pairing_fragment.xml
index 24fcd83..7fbe229 100644
--- a/nearby/halfsheet/res/layout/fast_pair_device_pairing_fragment.xml
+++ b/nearby/halfsheet/res/layout/fast_pair_device_pairing_fragment.xml
@@ -73,50 +73,67 @@
     <RelativeLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:orientation="horizontal"
         app:layout_constraintTop_toBottomOf="@+id/connect_progressbar"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent">
+
       <ImageView
           android:id="@+id/info_icon"
+          android:layout_width="24dp"
+          android:layout_height="24dp"
+          app:srcCompat="@drawable/fast_pair_ic_info"
           android:layout_centerInParent="true"
           android:contentDescription="@null"
           android:layout_marginEnd="10dp"
           android:layout_toStartOf="@id/connect_btn"
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"/>
-      <Button
+          android:visibility="invisible" />
+
+      <com.google.android.material.button.MaterialButton
           android:id="@+id/connect_btn"
-          android:text="@string/common_connect"
-          android:layout_height="wrap_content"
           android:layout_width="@dimen/fast_pair_half_sheet_image_size"
+          android:layout_height="wrap_content"
+          android:text="@string/paring_action_connect"
           android:layout_centerInParent="true"
-          android:background="@color/fast_pair_half_sheet_button_color"
           style="@style/HalfSheetButton" />
+
     </RelativeLayout>
 
-    <Button
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/settings_btn"
+        android:text="@string/paring_action_settings"
+        android:layout_height="wrap_content"
+        android:layout_width="@dimen/fast_pair_half_sheet_image_size"
+        app:layout_constraintTop_toBottomOf="@+id/connect_progressbar"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        android:visibility="invisible"
+        style="@style/HalfSheetButton" />
+
+    <com.google.android.material.button.MaterialButton
         android:id="@+id/cancel_btn"
-        android:text="@string/common_done"
-        android:visibility="gone"
+        android:text="@string/paring_action_done"
+        android:visibility="invisible"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"
         android:gravity="start|center_vertical"
         android:layout_marginTop="6dp"
-        android:layout_marginBottom="16dp"
         style="@style/HalfSheetButtonBorderless"/>
 
-    <Button
+    <com.google.android.material.button.MaterialButton
         android:id="@+id/setup_btn"
+        android:text="@string/paring_action_launch"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"
         android:layout_marginTop="6dp"
         android:layout_marginBottom="16dp"
         android:background="@color/fast_pair_half_sheet_button_color"
-        android:visibility="gone"
+        android:visibility="invisible"
         android:layout_height="@dimen/fast_pair_half_sheet_bottom_button_height"
         android:layout_width="wrap_content"
         style="@style/HalfSheetButton" />
+
   </androidx.constraintlayout.widget.ConstraintLayout>
 
 </LinearLayout>
diff --git a/nearby/halfsheet/res/values/strings.xml b/nearby/halfsheet/res/values/strings.xml
index 12e3c23..01a82e4 100644
--- a/nearby/halfsheet/res/values/strings.xml
+++ b/nearby/halfsheet/res/values/strings.xml
@@ -16,9 +16,57 @@
 
 <resources>
 
-    <string name="common_done" description="After pairing process finish button text to dismiss halfsheet">Done</string>
-    <string name="common_save">Save</string>
-    <string name="common_connect" description="Button text to start connecting process">Connect</string>
-    <string name="fast_pair_app_launch_button" description="String on app launch half sheet button.">Set up</string>
+    <!--
+      ============================================================
+      PAIRING FRAGMENT
+      ============================================================
+    -->
 
+    <!--
+      A button shown to remind user setup is in progress. [CHAR LIMIT=30]
+    -->
+    <string name="fast_pair_setup_in_progress">Starting Setup&#x2026;</string>
+    <!--
+      Title text shown to remind user to setup a device through companion app. [CHAR LIMIT=40]
+    -->
+    <string name="fast_pair_title_setup">Set up device</string>
+    <!--
+      Title after we successfully pair with the audio device
+      [CHAR LIMIT=30]
+    -->
+    <string name="fast_pair_device_ready">Device connected</string>
+    <!-- Title text shown when peripheral device fail to connect to phone. [CHAR_LIMIT=30] -->
+    <string name="fast_pair_title_fail">Couldn\'t connect</string>
+
+    <!--
+      ============================================================
+      MISCELLANEOUS
+      ============================================================
+    -->
+
+    <!--
+      A button shown after paring process to dismiss the current activity.
+      [CHAR LIMIT=30]
+    -->
+    <string name="paring_action_done">Done</string>
+    <!--
+      A button shown for retroactive paring.
+      [CHAR LIMIT=30]
+     -->
+    <string name="paring_action_save">Save</string>
+    <!--
+      A button to start connecting process.
+      [CHAR LIMIT=30]
+     -->
+    <string name="paring_action_connect">Connect</string>
+    <!--
+      A button to launch a companion app.
+      [CHAR LIMIT=30]
+    -->
+    <string name="paring_action_launch">Set up</string>
+    <!--
+      A button to launch a bluetooth Settings page.
+      [CHAR LIMIT=20]
+    -->
+    <string name="paring_action_settings">Settings</string>
 </resources>
\ No newline at end of file
diff --git a/nearby/halfsheet/res/values/styles.xml b/nearby/halfsheet/res/values/styles.xml
index b48da70..917bb63 100644
--- a/nearby/halfsheet/res/values/styles.xml
+++ b/nearby/halfsheet/res/values/styles.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-  <style name="HalfSheetStyle" parent="Theme.MaterialComponents.DayNight.NoActionBar">
+  <style name="HalfSheetStyle" parent="Theme.Material3.DayNight.NoActionBar">
     <item name="android:windowFrame">@null</item>
     <item name="android:windowBackground">@android:color/transparent</item>
     <item name="android:windowEnterAnimation">@anim/fast_pair_half_sheet_slide_in</item>
@@ -14,7 +14,7 @@
     <item name="android:windowTranslucentNavigation">true</item>
   </style>
 
-  <style name="HalfSheetButton" parent="@style/Widget.MaterialComponents.Button.TextButton">
+  <style name="HalfSheetButton" parent="@style/Widget.Material3.Button.TonalButton">
     <item name="android:textColor">@color/fast_pair_half_sheet_button_accent_text</item>
     <item name="android:backgroundTint">@color/fast_pair_half_sheet_button_color</item>
     <item name="android:textSize">@dimen/fast_pair_notification_text_size</item>
@@ -23,8 +23,7 @@
     <item name="android:textAllCaps">false</item>
   </style>
 
-  <style name="HalfSheetButtonBorderless"
-      parent="@style/Widget.MaterialComponents.Button.OutlinedButton">
+  <style name="HalfSheetButtonBorderless" parent="@style/Widget.Material3.Button.OutlinedButton">
     <item name="android:textColor">@color/fast_pair_half_sheet_button_text</item>
     <item name="android:strokeColor">@color/fast_pair_half_sheet_button_color</item>
     <item name="android:textAllCaps">false</item>
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java
index c495c35..9507b9b 100644
--- a/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java
@@ -19,22 +19,18 @@
 import static com.android.nearby.halfsheet.fragment.DevicePairingFragment.APP_LAUNCH_FRAGMENT_TYPE;
 import static com.android.server.nearby.common.bluetooth.fastpair.FastPairConstants.EXTRA_MODEL_ID;
 import static com.android.server.nearby.common.fastpair.service.UserActionHandlerBase.EXTRA_MAC_ADDRESS;
-import static com.android.server.nearby.fastpair.Constant.ACTION_FAST_PAIR_HALF_SHEET_BAN_STATE_RESET;
 import static com.android.server.nearby.fastpair.Constant.ACTION_FAST_PAIR_HALF_SHEET_CANCEL;
 import static com.android.server.nearby.fastpair.Constant.DEVICE_PAIRING_FRAGMENT_TYPE;
 import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_INFO;
 import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_TYPE;
 
-import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.FragmentActivity;
 
 import com.android.nearby.halfsheet.fragment.DevicePairingFragment;
@@ -48,14 +44,12 @@
 import service.proto.Cache;
 
 /**
- * Half sheet activity to show pairing ux.
+ * A class show Fast Pair related information in Half sheet format.
  */
 public class HalfSheetActivity extends FragmentActivity {
 
-    public static final String EXTRA_HALF_SHEET_PENDING_INTENT_CALL_BACK =
-            "com.android.nearby.halfsheet.EXTRA_HALF_SHEET_PENDING_INTENT_CALL_BACK";
-    public static final String EXTRA_HALF_SHEET_PACKAGE_NAME =
-            "com.android.nearby.halfsheet.EXTRA_HALF_SHEET_PACKAGE_NAME";
+    public static final String TAG = "HalfSheetActivity";
+
     public static final String EXTRA_HALF_SHEET_CONTENT =
             "com.android.nearby.halfsheet.HALF_SHEET_CONTENT";
     public static final String EXTRA_TITLE =
@@ -72,38 +66,11 @@
             "com.android.nearby.halfsheet.EXTRA_HALF_SHEET_PAIRING_RESURFACE";
     public static final String ACTION_HALF_SHEET_FOREGROUND_STATE =
             "com.android.nearby.halfsheet.ACTION_HALF_SHEET_FOREGROUND_STATE";
-    public static final String ACTION_HALF_SHEET_BAN_ALL_ITEM =
-            "com.android.nearby.halfsheet.ACTION_HALF_SHEET_BAN_ALL_ITEM";
-    public static final String ACTION_HALF_SHEET_APP_LAUNCH_CLICKED =
-            "com.android.nearby.halfsheet.ACTION_HALF_SHEET_APP_LAUNCH_CLICKED";
-    public static final String ACTION_HALF_SHEET_WEAR_OS_CLICKED =
-            "com.android.nearby.halfsheet.ACTION_HALF_SHEET_WEAR_OS_CLICKED";
-    public static final String ACTION_HALF_SHEET_USER_COMPLETE_CONFIRMATION =
-            "com.android.nearby.halfsheet.ACTION_HALF_SHEET_USER_COMPLETE_CONFIRMATION";
-    public static final String ACTION_FAST_PAIR_HANDLE_CHIP_DEVICE =
-            "com.android.nearby.halfsheet.ACTION_FAST_PAIR_HANDLE_CHIP_DEVICE";
-    // Intent extra contains another intent that will trigger DiscoveryChimeraService to upload
-    // device
-    // information to the cloud.
-    public static final String EXTRA_HALF_SHEET_CLOUD_SYNC_INTENT =
-            "com.android.nearby.halfsheet.HALF_SHEET_CLOUD_SYNC_INTENT";
     // Intent extra contains the user gmail name eg. testaccount@gmail.com.
     public static final String EXTRA_HALF_SHEET_ACCOUNT_NAME =
             "com.android.nearby.halfsheet.HALF_SHEET_ACCOUNT_NAME";
     public static final String EXTRA_HALF_SHEET_FOREGROUND =
             "com.android.nearby.halfsheet.EXTRA_HALF_SHEET_FOREGROUND";
-    public static final String EXTRA_USER_CONSENT_SYNC_CONTACTS =
-            "com.android.nearby.halfsheet.EXTRA_USER_CONSENT_SYNC_CONTACTS";
-    public static final String EXTRA_USER_CONSENT_SYNC_SMS =
-            "com.android.nearby.halfsheet.EXTRA_USER_CONSENT_SYNC_SMS";
-    public static final String EXTRA_USER_CONFIRM_PASSKEY =
-            "com.android.nearby.halfsheet.EXTRA_USER_CONFIRM_PASSKEY";
-    public static final String CLASS_NAME =
-            "com.android.nearby.halfsheet.HalfSheetActivity";
-    public static final String ACTION_HALF_SHEET_STATUS_CHANGE =
-            "com.android.nearby.halfsheet.ACTION_HALF_SHEET_STATUS_CHANGE";
-    public static final String FINISHED_STATE = "FINISHED_STATE";
-    public static final String EXTRA_CLASSIC_MAC_ADDRESS = "EXTRA_CLASSIC_MAC_ADDRESS";
     public static final String ARG_FRAGMENT_STATE = "ARG_FRAGMENT_STATE";
     @Nullable
     private HalfSheetModuleFragment mHalfSheetModuleFragment;
@@ -128,7 +95,7 @@
                 mHalfSheetModuleFragment = DevicePairingFragment.newInstance(getIntent(),
                         savedInstanceState);
                 if (mHalfSheetModuleFragment == null) {
-                    Log.d("HalfSheetActivity", "device pairing fragment has error.");
+                    Log.d(TAG, "device pairing fragment has error.");
                     finish();
                     return;
                 }
@@ -136,13 +103,13 @@
             case APP_LAUNCH_FRAGMENT_TYPE:
                 // currentFragment = AppLaunchFragment.newInstance(getIntent());
                 if (mHalfSheetModuleFragment == null) {
-                    Log.v("HalfSheetActivity", "app launch fragment has error.");
+                    Log.v(TAG, "app launch fragment has error.");
                     finish();
                     return;
                 }
                 break;
             default:
-                Log.w("HalfSheetActivity", "there is no valid type for half sheet");
+                Log.w(TAG, "there is no valid type for half sheet");
                 finish();
                 return;
         }
@@ -159,23 +126,19 @@
         findViewById(R.id.background).setOnClickListener(v -> onCancelClicked());
         findViewById(R.id.card)
                 .setOnClickListener(
-                        v -> Log.v("HalfSheetActivity", "card view is clicked noop"));
+                        v -> Log.v(TAG, "card view is clicked noop"));
         try {
             mScanFastPairStoreItem =
                     Cache.ScanFastPairStoreItem.parseFrom(infoArray);
         } catch (InvalidProtocolBufferException e) {
             Log.w(
-                    "HalfSheetActivity", "error happens when pass info to half sheet");
+                    TAG, "error happens when pass info to half sheet");
         }
     }
 
     @Override
     protected void onStart() {
         super.onStart();
-        BroadcastUtils.sendBroadcast(
-                this,
-                new Intent(ACTION_HALF_SHEET_FOREGROUND_STATE)
-                        .putExtra(EXTRA_HALF_SHEET_FOREGROUND, true));
     }
 
     @Override
@@ -217,17 +180,18 @@
                         mScanFastPairStoreItem.getAddress())
                         && testScanFastPairStoreItem.getModelId().equals(
                         mScanFastPairStoreItem.getModelId())) {
-                    Log.d("HalfSheetActivity", "possible factory reset happens");
+                    Log.d(TAG, "possible factory reset happens");
                     halfSheetStateChange();
                 }
             } catch (InvalidProtocolBufferException | NullPointerException e) {
-                Log.w("HalfSheetActivity", "error happens when pass info to half sheet");
+                Log.w(TAG, "error happens when pass info to half sheet");
             }
         }
     }
 
     /** This function should be called when user click empty area and cancel button. */
     public void onCancelClicked() {
+        Log.d(TAG, "Cancels the half sheet and paring.");
         sendHalfSheetCancelBroadcast();
         finish();
     }
@@ -241,20 +205,6 @@
         finish();
     }
 
-    /**
-     * Change the half sheet ban state to active sometimes users leave half sheet to go to fast pair
-     * info page we do not want the behavior to be counted as dismiss.
-     */
-    public void sendBanStateResetBroadcast() {
-        if (mScanFastPairStoreItem != null) {
-            BroadcastUtils.sendBroadcast(
-                    this,
-                    new Intent(ACTION_FAST_PAIR_HALF_SHEET_BAN_STATE_RESET)
-                            .putExtra(EXTRA_MODEL_ID,
-                                    mScanFastPairStoreItem.getModelId().toLowerCase(Locale.ROOT)));
-        }
-    }
-
     private void sendHalfSheetCancelBroadcast() {
         BroadcastUtils.sendBroadcast(
                 this,
@@ -280,31 +230,10 @@
         }
     }
 
-    @Nullable
-    @VisibleForTesting
-    public HalfSheetModuleFragment getFragmentModel() {
-        return mHalfSheetModuleFragment;
-    }
-
     @Override
     public void setTitle(CharSequence title) {
         super.setTitle(title);
         TextView toolbarTitle = findViewById(R.id.toolbar_title);
         toolbarTitle.setText(title);
     }
-
-
-    /**
-     * This method converts dp unit to equivalent pixels, depending on device density.
-     *
-     * @param dp      A value in dp (density independent pixels) unit, which we need to convert into
-     *                pixels
-     * @param context Context to get resources and device specific display metrics
-     * @return A float value to represent px equivalent to dp depending on device density
-     */
-    private float convertDpToPixel(float dp, Context context) {
-        return dp
-                * ((float) context.getResources().getDisplayMetrics().densityDpi
-                / DisplayMetrics.DENSITY_DEFAULT);
-    }
 }
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/DevicePairingFragment.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/DevicePairingFragment.java
index 74530de..a62c8cc 100644
--- a/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/DevicePairingFragment.java
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/DevicePairingFragment.java
@@ -17,44 +17,35 @@
 
 import static android.text.TextUtils.isEmpty;
 
-import static com.android.nearby.halfsheet.HalfSheetActivity.ACTION_HALF_SHEET_STATUS_CHANGE;
 import static com.android.nearby.halfsheet.HalfSheetActivity.ARG_FRAGMENT_STATE;
-import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_CLASSIC_MAC_ADDRESS;
 import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_DESCRIPTION;
 import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_ACCOUNT_NAME;
 import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_CONTENT;
 import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_ID;
-import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_IS_RETROACTIVE;
-import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR;
-import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_PAIRING_RESURFACE;
 import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_TITLE;
-import static com.android.nearby.halfsheet.HalfSheetActivity.FINISHED_STATE;
+import static com.android.nearby.halfsheet.HalfSheetActivity.TAG;
+import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.FAILED;
+import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.FOUND_DEVICE;
 import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.NOT_STARTED;
-import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.RESULT_FAILURE;
-import static com.android.server.nearby.common.bluetooth.fastpair.FastPairConstants.EXTRA_MODEL_ID;
-import static com.android.server.nearby.fastpair.Constant.DISMISS;
+import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.PAIRED_LAUNCHABLE;
+import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.PAIRED_UNLAUNCHABLE;
+import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.PAIRING;
 import static com.android.server.nearby.fastpair.Constant.EXTRA_BINDER;
 import static com.android.server.nearby.fastpair.Constant.EXTRA_BUNDLE;
 import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_INFO;
-import static com.android.server.nearby.fastpair.Constant.FAIL_STATE;
-import static com.android.server.nearby.fastpair.Constant.SUCCESS_STATE;
-import static com.android.server.nearby.fastpair.UserActionHandler.ACTION_FAST_PAIR;
-import static com.android.server.nearby.fastpair.UserActionHandler.EXTRA_PRIVATE_BLE_ADDRESS;
 
-import android.animation.AnimatorSet;
-import android.app.Activity;
-import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.Configuration;
-import android.nearby.IFastPairHalfSheetCallback;
+import android.graphics.Bitmap;
+import android.nearby.FastPairClient;
+import android.nearby.FastPairDevice;
+import android.nearby.FastPairStatusCallback;
+import android.nearby.NearbyDevice;
+import android.nearby.PairStatusMetadata;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -69,12 +60,13 @@
 
 import com.android.nearby.halfsheet.HalfSheetActivity;
 import com.android.nearby.halfsheet.R;
-import com.android.nearby.halfsheet.utils.BroadcastUtils;
 import com.android.nearby.halfsheet.utils.FastPairUtils;
-import com.android.server.nearby.fastpair.UserActionHandler;
+import com.android.nearby.halfsheet.utils.IconUtils;
 
 import com.google.protobuf.InvalidProtocolBufferException;
 
+import java.util.Objects;
+
 import service.proto.Cache.ScanFastPairStoreItem;
 
 /**
@@ -83,36 +75,38 @@
  * <p>This fragment will handle initial pairing subsequent pairing and retroactive pairing.
  */
 @SuppressWarnings("nullness")
-public class DevicePairingFragment extends HalfSheetModuleFragment {
+public class DevicePairingFragment extends HalfSheetModuleFragment implements
+        FastPairStatusCallback {
+    private TextView mTitleView;
+    private TextView mSubTitleView;
+    private ImageView mImage;
+
     private Button mConnectButton;
     private Button mSetupButton;
     private Button mCancelButton;
+    // Opens Bluetooth Settings.
+    private Button mSettingsButton;
     private ImageView mInfoIconButton;
     private ProgressBar mConnectProgressBar;
-    private View mRootView;
-    private TextView mSubTitle;
-    private TextView mTitle;
-    private ImageView mImage;
-    private ScanFastPairStoreItem mScanFastPairStoreItem;
-    // This open companion app intent will be triggered after user finish Fast Pair.
-    private Intent mOpenCompanionAppIntent;
-    // Indicates that the setup button is clicked before.
-    private boolean mSetupButtonClicked = false;
-    private boolean mIsSubsequentPair = false;
-    private boolean mIsPairingResurface = false;
-    private String mBluetoothMacAddress = "";
-    private HalfSheetFragmentState mFragmentState = NOT_STARTED;
-    private AnimatorSet mAnimatorSet = new AnimatorSet();
-    // True means pairing was successful and false means failed.
-    private Boolean mPairingResult = false;
+
     private Bundle mBundle;
 
-    public static final String APP_LAUNCH_FRAGMENT_TYPE = "APP_LAUNCH";
-    public static final String FAST_PAIR_CONSENT_FRAGMENT_TYPE = "FAST_PAIR_CONSENT";
-    private static final String ARG_SETUP_BUTTON_CLICKED = "SETUP_BUTTON_CLICKED";
-    public static final String RESULT_FAIL = "RESULT_FAIL";
-    private static final String ARG_PAIRING_RESULT = "PAIRING_RESULT";
+    private ScanFastPairStoreItem mScanFastPairStoreItem;
+    private FastPairClient mFastPairClient;
 
+    private @PairStatusMetadata.Status int mPairStatus = PairStatusMetadata.Status.UNKNOWN;
+    // True when there is a companion app to open.
+    private boolean mIsLaunchable;
+    private boolean mIsConnecting;
+    // Indicates that the setup button is clicked before.
+    private boolean mSetupButtonClicked = false;
+
+    // Holds the new text while we transition between the two.
+    private static final int TAG_PENDING_TEXT = R.id.toolbar_title;
+    public static final String APP_LAUNCH_FRAGMENT_TYPE = "APP_LAUNCH";
+
+    private static final String ARG_SETUP_BUTTON_CLICKED = "SETUP_BUTTON_CLICKED";
+    private static final String ARG_PAIRING_RESULT = "PAIRING_RESULT";
 
     /**
      * Create certain fragment according to the intent.
@@ -122,23 +116,15 @@
             Intent intent, @Nullable Bundle saveInstanceStates) {
         Bundle args = new Bundle();
         byte[] infoArray = intent.getByteArrayExtra(EXTRA_HALF_SHEET_INFO);
-        boolean isRetroactive = intent.getBooleanExtra(EXTRA_HALF_SHEET_IS_RETROACTIVE, false);
-        boolean isSubsequentPair = intent.getBooleanExtra(EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR,
-                false);
-        boolean isPairingResurface = intent.getBooleanExtra(EXTRA_HALF_SHEET_PAIRING_RESURFACE,
-                false);
+
         Bundle bundle = intent.getBundleExtra(EXTRA_BUNDLE);
         String title = intent.getStringExtra(EXTRA_TITLE);
         String description = intent.getStringExtra(EXTRA_DESCRIPTION);
         String accountName = intent.getStringExtra(EXTRA_HALF_SHEET_ACCOUNT_NAME);
         String result = intent.getStringExtra(EXTRA_HALF_SHEET_CONTENT);
-        String publicAddress = intent.getStringExtra(EXTRA_CLASSIC_MAC_ADDRESS);
         int halfSheetId = intent.getIntExtra(EXTRA_HALF_SHEET_ID, 0);
 
         args.putByteArray(EXTRA_HALF_SHEET_INFO, infoArray);
-        args.putBoolean(EXTRA_HALF_SHEET_IS_RETROACTIVE, isRetroactive);
-        args.putBoolean(EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR, isSubsequentPair);
-        args.putBoolean(EXTRA_HALF_SHEET_PAIRING_RESURFACE, isPairingResurface);
         args.putString(EXTRA_HALF_SHEET_ACCOUNT_NAME, accountName);
         args.putString(EXTRA_TITLE, title);
         args.putString(EXTRA_DESCRIPTION, description);
@@ -180,21 +166,22 @@
     public View onCreateView(
             LayoutInflater inflater, @Nullable ViewGroup container,
             @Nullable Bundle savedInstanceState) {
-        mRootView =
-                inflater.inflate(
-                        R.layout.fast_pair_device_pairing_fragment, container, /* attachToRoot= */
-                        false);
+        /* attachToRoot= */
+        View rootView = inflater.inflate(
+                R.layout.fast_pair_device_pairing_fragment, container, /* attachToRoot= */
+                false);
         if (getContext() == null) {
-            Log.d("DevicePairingFragment", "can't find the attached activity");
-            return mRootView;
+            Log.d(TAG, "can't find the attached activity");
+            return rootView;
         }
+
         Bundle args = getArguments();
         byte[] storeFastPairItemBytesArray = args.getByteArray(EXTRA_HALF_SHEET_INFO);
-        boolean isRetroactive = args.getBoolean(EXTRA_HALF_SHEET_IS_RETROACTIVE);
-        mIsSubsequentPair = args.getBoolean(EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR);
-        mIsPairingResurface = args.getBoolean(EXTRA_HALF_SHEET_PAIRING_RESURFACE);
-        String accountName = args.getString(EXTRA_HALF_SHEET_ACCOUNT_NAME);
         mBundle = args.getBundle(EXTRA_BUNDLE);
+        if (mBundle != null) {
+            mFastPairClient = new FastPairClient(getContext(), mBundle.getBinder(EXTRA_BINDER));
+            mFastPairClient.registerHalfSheet(this);
+        }
         if (args.containsKey(ARG_FRAGMENT_STATE)) {
             mFragmentState = (HalfSheetFragmentState) args.getSerializable(ARG_FRAGMENT_STATE);
         }
@@ -202,86 +189,53 @@
             mSetupButtonClicked = args.getBoolean(ARG_SETUP_BUTTON_CLICKED);
         }
         if (args.containsKey(ARG_PAIRING_RESULT)) {
-            mPairingResult = args.getBoolean(ARG_PAIRING_RESULT);
-        } else {
-            mPairingResult = false;
+            mPairStatus = args.getInt(ARG_PAIRING_RESULT);
         }
 
-        // title = ((FragmentActivity) getContext()).findViewById(R.id.toolbar_title);
-        mConnectButton = mRootView.findViewById(R.id.connect_btn);
-        mImage = mRootView.findViewById(R.id.pairing_pic);
+        // Initiate views.
+        mTitleView = Objects.requireNonNull(getActivity()).findViewById(R.id.toolbar_title);
+        mSubTitleView = rootView.findViewById(R.id.header_subtitle);
+        mImage = rootView.findViewById(R.id.pairing_pic);
+        mConnectProgressBar = rootView.findViewById(R.id.connect_progressbar);
+        mConnectButton = rootView.findViewById(R.id.connect_btn);
+        mCancelButton = rootView.findViewById(R.id.cancel_btn);
+        mSettingsButton = rootView.findViewById(R.id.settings_btn);
+        mSetupButton = rootView.findViewById(R.id.setup_btn);
+        mInfoIconButton = rootView.findViewById(R.id.info_icon);
+        mInfoIconButton.setImageResource(R.drawable.fast_pair_ic_info);
 
-        mConnectProgressBar = mRootView.findViewById(R.id.connect_progressbar);
-        mConnectProgressBar.setVisibility(View.INVISIBLE);
+        try {
+            setScanFastPairStoreItem(ScanFastPairStoreItem.parseFrom(storeFastPairItemBytesArray));
+        } catch (InvalidProtocolBufferException e) {
+            Log.w(TAG,
+                    "DevicePairingFragment: error happens when pass info to half sheet");
+            return rootView;
+        }
 
+        // Config for landscape mode
         DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
         if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            mRootView.getLayoutParams().height = displayMetrics.heightPixels * 4 / 5;
-            mRootView.getLayoutParams().width = displayMetrics.heightPixels * 4 / 5;
+            rootView.getLayoutParams().height = displayMetrics.heightPixels * 4 / 5;
+            rootView.getLayoutParams().width = displayMetrics.heightPixels * 4 / 5;
             mImage.getLayoutParams().height = displayMetrics.heightPixels / 2;
             mImage.getLayoutParams().width = displayMetrics.heightPixels / 2;
             mConnectProgressBar.getLayoutParams().width = displayMetrics.heightPixels / 2;
             mConnectButton.getLayoutParams().width = displayMetrics.heightPixels / 2;
+            //TODO(b/213373051): Add cancel button
         }
 
-        mCancelButton = mRootView.findViewById(R.id.cancel_btn);
-        mSetupButton = mRootView.findViewById(R.id.setup_btn);
-        mInfoIconButton = mRootView.findViewById(R.id.info_icon);
-        mSubTitle = mRootView.findViewById(R.id.header_subtitle);
-        mSetupButton.setVisibility(View.GONE);
-        mInfoIconButton.setVisibility(View.GONE);
-
-        try {
-            if (storeFastPairItemBytesArray != null) {
-                mScanFastPairStoreItem =
-                        ScanFastPairStoreItem.parseFrom(storeFastPairItemBytesArray);
-            }
-
-            // If the fragmentState is not NOT_STARTED, it is because the fragment was just
-            // resumed from
-            // configuration change (e.g. rotating the screen or half-sheet resurface). Let's
-            // recover the
-            // UI directly.
-            if (mFragmentState != NOT_STARTED) {
-                switch (mFragmentState) {
-                    case PAIRING:
-                        Log.d("DevicePairingFragment", "redraw for PAIRING state.");
-                        return mRootView;
-                    case RESULT_SUCCESS:
-                        Log.d("DevicePairingFragment", "redraw for RESULT_SUCCESS state.");
-                        return mRootView;
-                    case RESULT_FAILURE:
-                        Log.d("DevicePairingFragment", "redraw for RESULT_FAILURE state.");
-                        return mRootView;
-                    default:
-                        // fall-out
-                        Log.d("DevicePairingFragment",
-                                "DevicePairingFragment: not supported state");
-                }
-            }
-            if (mIsPairingResurface) {
-                // Since the Settings contextual card has sent the pairing intent, we don't send the
-                // pairing intent here.
-                onConnectClick(/* sendPairingIntent= */ false);
-            } else {
-                mSubTitle.setText(this.getArguments().getString(EXTRA_DESCRIPTION));
-                mSubTitle.setText("");
-                mConnectButton.setOnClickListener(
-                        v -> onConnectClick(/* sendPairingIntent= */ true));
-                // Pairing fail half sheet resurface
-                if (this.getArguments().getString(EXTRA_HALF_SHEET_CONTENT).equals(RESULT_FAIL)) {
-                    mFragmentState = RESULT_FAILURE;
-                    showFailInfo();
-                } else {
-                    mConnectButton.setOnClickListener(
-                            v -> onConnectClick(/* sendPairingIntent= */ true));
-                }
-            }
-        } catch (InvalidProtocolBufferException e) {
-            Log.w("DevicePairingFragment",
-                    "DevicePairingFragment: error happens when pass info to half sheet");
+        Bitmap icon = IconUtils.getIcon(mScanFastPairStoreItem.getIconPng().toByteArray(),
+                mScanFastPairStoreItem.getIconPng().size());
+        if (icon != null) {
+            mImage.setImageBitmap(icon);
         }
-        return mRootView;
+        mConnectButton.setOnClickListener(v -> onConnectClick());
+        mCancelButton.setOnClickListener(v ->
+                ((HalfSheetActivity) getActivity()).onCancelClicked());
+        mSettingsButton.setOnClickListener(v -> onSettingsClicked());
+        mSetupButton.setOnClickListener(v -> onSetupClick());
+
+        return rootView;
     }
 
     @Override
@@ -294,19 +248,8 @@
     @Override
     public void onStart() {
         super.onStart();
-        if (getContext() != null) {
-            IntentFilter intentFilter = new IntentFilter();
-            intentFilter.addAction(ACTION_HALF_SHEET_STATUS_CHANGE);
-            BroadcastUtils.registerReceiver(getContext(), mHalfSheetChangeReceiver, intentFilter);
-        }
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        if (getContext() != null) {
-            BroadcastUtils.unregisterReceiver(getContext(), mHalfSheetChangeReceiver);
-        }
+        Log.v(TAG, "onStart: invalidate states");
+        invalidateState();
     }
 
     @Override
@@ -315,118 +258,229 @@
 
         savedInstanceState.putSerializable(ARG_FRAGMENT_STATE, mFragmentState);
         savedInstanceState.putBoolean(ARG_SETUP_BUTTON_CLICKED, mSetupButtonClicked);
-        savedInstanceState.putBoolean(ARG_PAIRING_RESULT, mPairingResult);
-
-
+        savedInstanceState.putInt(ARG_PAIRING_RESULT, mPairStatus);
     }
 
-    @Nullable
-    private Intent createCompletionIntent(@Nullable String companionApp, @Nullable String address) {
-        if (isEmpty(companionApp)) {
-            return null;
-        } else if (FastPairUtils.isAppInstalled(getContext(), companionApp)
-                && isLaunchable(companionApp)) {
-            mOpenCompanionAppIntent = createCompanionAppIntent(companionApp, address);
-            return mOpenCompanionAppIntent;
-        } else {
-            return null;
-        }
-    }
-
-    @Nullable
-    private Intent createCompanionAppIntent(String packageName, @Nullable String address) {
-        return createCompanionAppIntent(getContext(), packageName, address);
-    }
-
-    @Nullable
-    private static Intent createCompanionAppIntent(
-            Context context, String packageName, @Nullable String address) {
-        Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
-        BluetoothManager manager = context.getSystemService(BluetoothManager.class);
-        if (address != null && manager != null) {
-            BluetoothAdapter adapter = manager.getAdapter();
-            if (intent != null && adapter != null) {
-                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, adapter.getRemoteDevice(address));
-            }
-        }
-        return intent;
-    }
-
-    private void onConnectClick(boolean sendPairingIntent) {
-        if (mScanFastPairStoreItem == null) {
-            Log.w("DevicePairingFragment", "No pairing related information in half sheet");
-            return;
-        }
-
-        Log.d("FastPairHalfSheet", "on connect click");
-        // Allow user to setup device before connection setup.
-        // showPairingLastPhase();
-        ((Activity) getContext())
-                .findViewById(R.id.background)
-                .setOnClickListener(
-                        v ->
-                                Log.d("DevicePairingFragment",
-                                        "DevicePairingFragment: tap empty area do not dismiss "
-                                                + "half sheet when pairing."));
-        if (sendPairingIntent) {
-            try {
-                Log.d("FastPairHalfSheet", "on connect click");
-                Intent intent =
-                        new Intent(ACTION_FAST_PAIR)
-                                // Using the DiscoveryChimeraService notification id for
-                                // backwards compat
-                                .putExtra(
-                                        UserActionHandler.EXTRA_DISCOVERY_ITEM,
-                                        FastPairUtils.convertFrom(
-                                                mScanFastPairStoreItem).toByteArray())
-                                .putExtra(EXTRA_MODEL_ID, mScanFastPairStoreItem.getModelId())
-                                .putExtra(EXTRA_PRIVATE_BLE_ADDRESS,
-                                        mScanFastPairStoreItem.getAddress());
-                IFastPairHalfSheetCallback.Stub.asInterface(mBundle.getBinder(EXTRA_BINDER))
-                        .onHalfSheetConnectionConfirm(intent);
-            } catch (RemoteException e) {
-                Log.d("FastPairHalfSheet", "invoke callback fall");
-            }
-        }
-    }
-
-    private void onFailConnectClick() {
+    private void onSettingsClicked() {
         startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
     }
 
-    private final BroadcastReceiver mHalfSheetChangeReceiver =
-            new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    if (!ACTION_HALF_SHEET_STATUS_CHANGE.equals(intent.getAction())) {
-                        return;
-                    }
-                    if (SUCCESS_STATE.equals(intent.getStringExtra(FINISHED_STATE))) {
-                        mBluetoothMacAddress = intent.getStringExtra(EXTRA_CLASSIC_MAC_ADDRESS);
-                        showSuccessInfo();
-                        if (mOpenCompanionAppIntent != null) {
-                            //((HalfSheetActivity) getContext()).halfSheetStateChange();
-                            String companionApp =
-                                    FastPairUtils.getCompanionAppFromActionUrl(
-                                            mScanFastPairStoreItem.getActionUrl());
-                            // Redirect user to companion app if user choose to setup the app.
-                            // Recreate the intent
-                            // since the correct mac address just populated.
-                            startActivity(
-                                    createCompletionIntent(companionApp, mBluetoothMacAddress));
-                        }
-                    } else if (FAIL_STATE.equals(intent.getStringExtra(FINISHED_STATE))) {
-                        showFailInfo();
-                    } else if (DISMISS.equals(intent.getStringExtra(FINISHED_STATE))) {
-                        if (getContext() != null) {
-                            HalfSheetActivity activity = (HalfSheetActivity) getContext();
-                            activity.finish();
-                        }
-                    }
-                }
-            };
+    private void onSetupClick() {
+        String companionApp =
+                FastPairUtils.getCompanionAppFromActionUrl(mScanFastPairStoreItem.getActionUrl());
+        Intent intent =
+                FastPairUtils.createCompanionAppIntent(
+                        Objects.requireNonNull(getContext()),
+                        companionApp,
+                        mScanFastPairStoreItem.getAddress());
+        mSetupButtonClicked = true;
+        if (mFragmentState == PAIRED_LAUNCHABLE) {
+            if (intent != null) {
+                startActivity(intent);
+            }
+        } else {
+            Log.d(TAG, "onSetupClick: State is " + mFragmentState);
+        }
+    }
 
-    private boolean isLaunchable(String companionApp) {
-        return createCompanionAppIntent(companionApp, null) != null;
+    private void onConnectClick() {
+        if (mScanFastPairStoreItem == null) {
+            Log.w(TAG, "No pairing related information in half sheet");
+            return;
+        }
+        if (getFragmentState() == PAIRING) {
+            return;
+        }
+        mIsConnecting = true;
+        invalidateState();
+        mFastPairClient.connect(
+                new FastPairDevice.Builder()
+                        .addMedium(NearbyDevice.Medium.BLE)
+                        .setBluetoothAddress(mScanFastPairStoreItem.getAddress())
+                        .setData(FastPairUtils.convertFrom(mScanFastPairStoreItem)
+                                .toByteArray())
+                        .build());
+    }
+
+    // Receives callback from service.
+    @Override
+    public void onPairUpdate(FastPairDevice fastPairDevice, PairStatusMetadata pairStatusMetadata) {
+        @PairStatusMetadata.Status int status = pairStatusMetadata.getStatus();
+        if (status == PairStatusMetadata.Status.DISMISS && getActivity() != null) {
+            getActivity().finish();
+        }
+        mIsConnecting = false;
+        mPairStatus = status;
+        invalidateState();
+    }
+
+    @Override
+    public void invalidateState() {
+        HalfSheetFragmentState newState = NOT_STARTED;
+        if (mIsConnecting) {
+            newState = PAIRING;
+        } else {
+            switch (mPairStatus) {
+                case PairStatusMetadata.Status.SUCCESS:
+                    newState = mIsLaunchable ? PAIRED_LAUNCHABLE : PAIRED_UNLAUNCHABLE;
+                    break;
+                case PairStatusMetadata.Status.FAIL:
+                    newState = FAILED;
+                    break;
+                default:
+                    if (mScanFastPairStoreItem != null) {
+                        newState = FOUND_DEVICE;
+                    }
+            }
+        }
+        if (newState == mFragmentState) {
+            return;
+        }
+        setState(newState);
+    }
+
+    @Override
+    public void setState(HalfSheetFragmentState state) {
+        super.setState(state);
+        invalidateTitles();
+        invalidateButtons();
+    }
+
+    private void setScanFastPairStoreItem(ScanFastPairStoreItem item) {
+        mScanFastPairStoreItem = item;
+        invalidateLaunchable();
+    }
+
+    private void invalidateLaunchable() {
+        String companionApp =
+                FastPairUtils.getCompanionAppFromActionUrl(mScanFastPairStoreItem.getActionUrl());
+        if (isEmpty(companionApp)) {
+            mIsLaunchable = false;
+            return;
+        }
+        mIsLaunchable =
+                FastPairUtils.isLaunchable(Objects.requireNonNull(getContext()), companionApp);
+    }
+
+    private void invalidateButtons() {
+        mConnectProgressBar.setVisibility(View.INVISIBLE);
+        mConnectButton.setVisibility(View.INVISIBLE);
+        mCancelButton.setVisibility(View.INVISIBLE);
+        mSetupButton.setVisibility(View.INVISIBLE);
+        mSettingsButton.setVisibility(View.INVISIBLE);
+        mInfoIconButton.setVisibility(View.INVISIBLE);
+
+        switch (mFragmentState) {
+            case FOUND_DEVICE:
+                mInfoIconButton.setVisibility(View.VISIBLE);
+                mConnectButton.setVisibility(View.VISIBLE);
+                break;
+            case PAIRING:
+                mConnectProgressBar.setVisibility(View.VISIBLE);
+                mCancelButton.setVisibility(View.VISIBLE);
+                setBackgroundClickable(false);
+                break;
+            case PAIRED_LAUNCHABLE:
+                mCancelButton.setVisibility(View.VISIBLE);
+                mSetupButton.setVisibility(View.VISIBLE);
+                setBackgroundClickable(true);
+                break;
+            case FAILED:
+                mSettingsButton.setVisibility(View.VISIBLE);
+                setBackgroundClickable(true);
+                break;
+            case NOT_STARTED:
+            case PAIRED_UNLAUNCHABLE:
+            default:
+                mCancelButton.setVisibility(View.VISIBLE);
+                setBackgroundClickable(true);
+        }
+    }
+
+    private void setBackgroundClickable(boolean isClickable) {
+        HalfSheetActivity activity = (HalfSheetActivity) getActivity();
+        if (activity == null) {
+            Log.w(TAG, "setBackgroundClickable: failed to set clickable to " + isClickable
+                    + " because cannot get HalfSheetActivity.");
+            return;
+        }
+        View background = activity.findViewById(R.id.background);
+        if (background == null) {
+            Log.w(TAG, "setBackgroundClickable: failed to set clickable to " + isClickable
+                    + " cannot find background at HalfSheetActivity.");
+            return;
+        }
+        Log.d(TAG, "setBackgroundClickable to " + isClickable);
+        background.setClickable(isClickable);
+    }
+
+    private void invalidateTitles() {
+        String newTitle = getTitle();
+        invalidateTextView(mTitleView, newTitle);
+        String newSubTitle = getSubTitle();
+        invalidateTextView(mSubTitleView, newSubTitle);
+    }
+
+    private void invalidateTextView(TextView textView, String newText) {
+        CharSequence oldText =
+                textView.getTag(TAG_PENDING_TEXT) != null
+                        ? (CharSequence) textView.getTag(TAG_PENDING_TEXT)
+                        : textView.getText();
+        if (TextUtils.equals(oldText, newText)) {
+            return;
+        }
+        if (TextUtils.isEmpty(oldText)) {
+            // First time run. Don't animate since there's nothing to animate from.
+            textView.setText(newText);
+        } else {
+            textView.setTag(TAG_PENDING_TEXT, newText);
+            textView
+                    .animate()
+                    .alpha(0f)
+                    .setDuration(TEXT_ANIMATION_DURATION_MILLISECONDS)
+                    .withEndAction(
+                            () -> {
+                                textView.setText(newText);
+                                textView
+                                        .animate()
+                                        .alpha(1f)
+                                        .setDuration(TEXT_ANIMATION_DURATION_MILLISECONDS);
+                            });
+        }
+    }
+
+    private String getTitle() {
+        switch (mFragmentState) {
+            case PAIRED_LAUNCHABLE:
+                return getString(R.string.fast_pair_title_setup);
+            case FAILED:
+                return getString(R.string.fast_pair_title_fail);
+            case FOUND_DEVICE:
+            case NOT_STARTED:
+            case PAIRED_UNLAUNCHABLE:
+            default:
+                return mScanFastPairStoreItem.getDeviceName();
+        }
+    }
+
+    private String getSubTitle() {
+        switch (mFragmentState) {
+            case PAIRED_LAUNCHABLE:
+                return String.format(
+                        mScanFastPairStoreItem
+                                .getFastPairStrings()
+                                .getPairingFinishedCompanionAppInstalled(),
+                        mScanFastPairStoreItem.getDeviceName());
+            case FAILED:
+                return mScanFastPairStoreItem.getFastPairStrings().getPairingFailDescription();
+            case PAIRED_UNLAUNCHABLE:
+                getString(R.string.fast_pair_device_ready);
+            // fall through
+            case FOUND_DEVICE:
+            case NOT_STARTED:
+                return mScanFastPairStoreItem.getFastPairStrings().getInitialPairingDescription();
+            default:
+                return "";
+        }
     }
 }
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/HalfSheetModuleFragment.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/HalfSheetModuleFragment.java
index 88caf95..f1db4d0 100644
--- a/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/HalfSheetModuleFragment.java
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/HalfSheetModuleFragment.java
@@ -15,17 +15,23 @@
  */
 package com.android.nearby.halfsheet.fragment;
 
+import static com.android.nearby.halfsheet.HalfSheetActivity.TAG;
+import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.NOT_STARTED;
+
 import android.os.Bundle;
+import android.util.Log;
 
 import androidx.annotation.Nullable;
 import androidx.fragment.app.Fragment;
 
 
 /** Base class for all of the half sheet fragment. */
-// TODO(b/177675274): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 public abstract class HalfSheetModuleFragment extends Fragment {
 
+    static final int TEXT_ANIMATION_DURATION_MILLISECONDS = 200;
+
+    HalfSheetFragmentState mFragmentState = NOT_STARTED;
+
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -38,26 +44,15 @@
 
     /** UI states of the half-sheet fragment. */
     public enum HalfSheetFragmentState {
-        NOT_STARTED,
-        SYNC_CONTACTS,
-        SYNC_SMS,
-        PROGRESSING,
-        CONFIRM_PASSKEY,
-        WRONG_PASSKEY,
-        PAIRING,
-        ADDITIONAL_SETUP_PROGRESS,
-        ADDITIONAL_SETUP_FINAL,
-        RESULT_SUCCESS,
-        RESULT_FAILURE,
-        FINISHED
-    }
-
-    /** Only used in {@link DevicePairingFragment} show pairing success info in half sheet. */
-    public void showSuccessInfo() {
-    }
-
-    /** Only used in {@link DevicePairingFragment} show pairing fail info in half sheet. */
-    public void showFailInfo() {
+        NOT_STARTED, // Initial status
+        FOUND_DEVICE, // When a device is found found from Nearby scan service
+        PAIRING, // When user taps 'Connect' and Fast Pair stars pairing process
+        PAIRED_LAUNCHABLE, // When pair successfully
+        // and we found a launchable companion app installed
+        PAIRED_UNLAUNCHABLE, // When pair successfully
+        // but we cannot find a companion app to launch it
+        FAILED, // When paring was failed
+        FINISHED // When the activity is about to end finished.
     }
 
     /**
@@ -67,6 +62,16 @@
      * activity.
      */
     public HalfSheetFragmentState getFragmentState() {
-        return HalfSheetFragmentState.NOT_STARTED;
+        return mFragmentState;
     }
+
+    void setState(HalfSheetFragmentState state) {
+        Log.v(TAG, "Settings state from " + mFragmentState + " to " + state);
+        mFragmentState = state;
+    }
+
+    /**
+     * Populate data to UI widgets according to the latest {@link HalfSheetFragmentState}.
+     */
+    abstract void invalidateState();
 }
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java
index cae2d8e..467997c 100644
--- a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java
@@ -16,30 +16,13 @@
 
 package com.android.nearby.halfsheet.utils;
 
-
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 
 /**
  * Broadcast util class
  */
 public class BroadcastUtils {
-    /**
-     * Registers a list of broadcast receiver.
-     */
-    public static void registerReceiver(
-            Context context, BroadcastReceiver receiver, IntentFilter intentFilter) {
-        context.registerReceiver(receiver, intentFilter);
-    }
-
-    /**
-     * Unregisters the already registered receiver.
-     */
-    public static void unregisterReceiver(Context context, BroadcastReceiver receiver) {
-        context.unregisterReceiver(receiver);
-    }
 
     /**
      * Helps send broadcast.
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/FastPairUtils.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/FastPairUtils.java
index fffb9e1..903ea90 100644
--- a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/FastPairUtils.java
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/FastPairUtils.java
@@ -38,8 +38,6 @@
  */
 public class FastPairUtils {
 
-    public static final String TAG = "HalfSheetActivity";
-
     /** FastPair util method check certain app is install on the device or not. */
     public static boolean isAppInstalled(Context context, String packageName) {
         try {
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/IconUtils.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/IconUtils.java
index 0521b7b..218c756 100644
--- a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/IconUtils.java
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/IconUtils.java
@@ -16,7 +16,7 @@
 
 package com.android.nearby.halfsheet.utils;
 
-import static com.android.nearby.halfsheet.utils.FastPairUtils.TAG;
+import static com.android.nearby.halfsheet.HalfSheetActivity.TAG;
 
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
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/FastPairAdvHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
index 6a296c2..f41715a 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
@@ -89,7 +89,7 @@
         } else {
             // Start to process bloom filter
             try {
-                Log.d(TAG, "account list size" + accountList.size());
+                Log.d(TAG, "account list size " + accountList.size());
                 byte[] bloomFilterByteArray = FastPairDecoder
                         .getBloomFilter(fastPairDevice.getData());
                 byte[] bloomFilterSalt = FastPairDecoder
@@ -100,8 +100,8 @@
                 }
                 for (Account account : accountList) {
                     List<Data.FastPairDeviceWithAccountKey> listDevices =
-                            FastPairDataProvider.getInstance().loadFastPairDevicesWithAccountKey(
-                                    account);
+                            FastPairDataProvider.getInstance()
+                                    .loadFastPairDeviceWithAccountKey(account);
                     Data.FastPairDeviceWithAccountKey recognizedDevice =
                             findRecognizedDevice(listDevices,
                                     new BloomFilter(bloomFilterByteArray,
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/service/java/com/android/server/nearby/provider/FastPairDataProvider.java b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
index 03484a0..f4db578 100644
--- a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.nearby.FastPairDataProviderBase;
+import android.nearby.aidl.ByteArrayParcel;
 import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel;
 import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataRequestParcel;
 import android.nearby.aidl.FastPairEligibleAccountsRequestParcel;
@@ -32,6 +33,7 @@
 import com.android.server.nearby.common.bloomfilter.BloomFilter;
 import com.android.server.nearby.fastpair.footprint.FastPairUploadInfo;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import service.proto.Data;
@@ -142,16 +144,38 @@
     }
 
     /**
-     * Loads FastPair devices for a given account.
+     * Loads FastPair device accountKeys for a given account, but not other detailed fields.
      *
      * @throws IllegalStateException If ProxyFastPairDataProvider is not available.
      */
-    public List<Data.FastPairDeviceWithAccountKey> loadFastPairDevicesWithAccountKey(
+    public List<Data.FastPairDeviceWithAccountKey> loadFastPairDeviceWithAccountKey(
             Account account) {
+        return loadFastPairDeviceWithAccountKey(account, new ArrayList<byte[]>(0));
+    }
+
+    /**
+     * Loads FastPair devices for a list of accountKeys of a given account.
+     *
+     * @param account The account of the FastPair devices.
+     * @param accountKeys The allow list of FastPair devices if it is not empty. Otherwise, the
+     *                    function returns accountKeys of all FastPair devices under the account,
+     *                    without detailed fields.
+     *
+     * @throws IllegalStateException If ProxyFastPairDataProvider is not available.
+     */
+    public List<Data.FastPairDeviceWithAccountKey> loadFastPairDeviceWithAccountKey(
+            Account account, List<byte[]> accountKeys) {
         if (mProxyFastPairDataProvider != null) {
             FastPairAccountDevicesMetadataRequestParcel requestParcel =
                     new FastPairAccountDevicesMetadataRequestParcel();
             requestParcel.account = account;
+            requestParcel.accountKeys = new ByteArrayParcel[accountKeys.size()];
+            int i = 0;
+            for (byte[] accountKey : accountKeys) {
+                requestParcel.accountKeys[i] = new ByteArrayParcel();
+                requestParcel.accountKeys[i].byteArray = accountKey;
+                i = i + 1;
+            }
             return Utils.convertToFastPairDevicesWithAccountKey(
                     mProxyFastPairDataProvider.loadFastPairAccountDevicesMetadata(requestParcel));
         }
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/FastPairDataProviderBaseTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/FastPairDataProviderBaseTest.java
index 71fc330..fd9e294 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/FastPairDataProviderBaseTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/FastPairDataProviderBaseTest.java
@@ -30,6 +30,7 @@
 import android.nearby.FastPairDeviceMetadata;
 import android.nearby.FastPairDiscoveryItem;
 import android.nearby.FastPairEligibleAccount;
+import android.nearby.aidl.ByteArrayParcel;
 import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel;
 import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel;
 import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataParcel;
@@ -85,6 +86,7 @@
     private static final Account MANAGE_ACCOUNT = new Account("ghi@gmail.com", "type3");
     private static final Account ACCOUNTDEVICES_METADATA_ACCOUNT =
             new Account("jk@gmail.com", "type4");
+    private static final int NUM_ACCOUNT_DEVICES = 2;
 
     private static final int ERROR_CODE_BAD_REQUEST =
             FastPairDataProviderBase.ERROR_CODE_BAD_REQUEST;
@@ -125,6 +127,7 @@
     private static final String WAIT_LAUNCH_COMPANION_APP_DESCRIPTION =
             "WAIT_LAUNCH_COMPANION_APP_DESCRIPTION";
     private static final byte[] ACCOUNT_KEY = new byte[] {3};
+    private static final byte[] ACCOUNT_KEY_2 = new byte[] {9, 3};
     private static final byte[] SHA256_ACCOUNT_KEY_PUBLIC_ADDRESS = new byte[] {2, 8};
     private static final byte[] REQUEST_MODEL_ID = new byte[] {1, 2, 3};
     private static final byte[] ANTI_SPOOFING_KEY = new byte[] {4, 5, 6};
@@ -533,6 +536,11 @@
                 new FastPairAccountDevicesMetadataRequestParcel();
 
         requestParcel.account = ACCOUNTDEVICES_METADATA_ACCOUNT;
+        requestParcel.accountKeys = new ByteArrayParcel[NUM_ACCOUNT_DEVICES];
+        requestParcel.accountKeys[0] = new ByteArrayParcel();
+        requestParcel.accountKeys[1] = new ByteArrayParcel();
+        requestParcel.accountKeys[0].byteArray = ACCOUNT_KEY;
+        requestParcel.accountKeys[1].byteArray = ACCOUNT_KEY_2;
 
         return requestParcel;
     }
@@ -793,6 +801,9 @@
     private static void ensureHappyPathAsExpected(
             FastPairDataProviderBase.FastPairAccountDevicesMetadataRequest request) {
         assertThat(request.getAccount()).isEqualTo(ACCOUNTDEVICES_METADATA_ACCOUNT);
+        assertThat(request.getAccountKeys().size()).isEqualTo(ACCOUNTKEY_DEVICE_NUM);
+        assertThat(request.getAccountKeys()).contains(ACCOUNT_KEY);
+        assertThat(request.getAccountKeys()).contains(ACCOUNT_KEY_2);
     }
 
     /* Verifies Happy Path FastPairEligibleAccountsRequest. */
diff --git a/nearby/tests/multidevices/clients/proguard.flags b/nearby/tests/multidevices/clients/proguard.flags
index ec8f526..2e34dce 100644
--- a/nearby/tests/multidevices/clients/proguard.flags
+++ b/nearby/tests/multidevices/clients/proguard.flags
@@ -3,6 +3,11 @@
      *;
 }
 
+# Keep simulator reflection callback.
+-keep class com.android.server.nearby.common.bluetooth.fastpair.testing.** {
+     *;
+}
+
 # Do not touch Mobly.
 -keep class com.google.android.mobly.** {
   *;
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/BluetoothStateChangeReceiver.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/BluetoothStateChangeReceiver.kt
index 25cc637..f9c77f7 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/BluetoothStateChangeReceiver.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/BluetoothStateChangeReceiver.kt
@@ -77,7 +77,7 @@
         when (intent.action) {
             BluetoothAdapter.ACTION_SCAN_MODE_CHANGED -> {
                 val scanMode =
-                    intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE)
+                    intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, SCAN_MODE_NONE)
                 val scanModeStr = scanModeToString(scanMode)
                 Log.i(TAG, "ACTION_SCAN_MODE_CHANGED, the new scanMode: $scanModeStr")
                 listener?.onScanModeChange(scanModeStr)
diff --git a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/fastpair/testing/FastPairSimulator.java b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/fastpair/testing/FastPairSimulator.java
index acc13e3..cf8be76 100644
--- a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/fastpair/testing/FastPairSimulator.java
+++ b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/fastpair/testing/FastPairSimulator.java
@@ -29,14 +29,15 @@
 import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_NOTIFY;
 import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_READ;
 import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_WRITE;
-import static com.android.server.nearby.common.bluetooth.fastpair.testing.RfcommServer.State.CONNECTED;
 
 import static com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption.AES_BLOCK_LENGTH;
 import static com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption.encrypt;
 import static com.android.server.nearby.common.bluetooth.fastpair.Bytes.toBytes;
 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.A2DP_SINK_SERVICE_UUID;
 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.TransportDiscoveryService.BLUETOOTH_SIG_ORGANIZATION_ID;
+import static com.android.server.nearby.common.bluetooth.fastpair.EllipticCurveDiffieHellmanExchange.PUBLIC_KEY_LENGTH;
 import static com.android.server.nearby.common.bluetooth.fastpair.MessageStreamHmacEncoder.SECTION_NONCE_LENGTH;
+import static com.android.server.nearby.common.bluetooth.fastpair.testing.RfcommServer.State.CONNECTED;
 import static com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothManager.wrap;
 
 import static com.google.common.io.BaseEncoding.base16;
@@ -55,7 +56,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.nearby.multidevices.fastpair.EventStreamProtocol;
+import android.nearby.multidevices.fastpair.EventStreamProtocol.AcknowledgementEventCode;
+import android.nearby.multidevices.fastpair.EventStreamProtocol.DeviceActionEventCode;
+import android.nearby.multidevices.fastpair.EventStreamProtocol.DeviceCapabilitySyncEventCode;
+import android.nearby.multidevices.fastpair.EventStreamProtocol.DeviceConfigurationEventCode;
+import android.nearby.multidevices.fastpair.EventStreamProtocol.DeviceEventCode;
+import android.nearby.multidevices.fastpair.EventStreamProtocol.EventGroup;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.os.Handler;
@@ -68,6 +74,8 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.core.util.Consumer;
 
+import com.android.server.nearby.common.bloomfilter.BloomFilter;
+import com.android.server.nearby.common.bloomfilter.FastPairBloomFilterHasher;
 import com.android.server.nearby.common.bluetooth.BluetoothException;
 import com.android.server.nearby.common.bluetooth.BluetoothGattException;
 import com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption;
@@ -83,6 +91,8 @@
 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.NameCharacteristic;
 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.PasskeyCharacteristic;
 import com.android.server.nearby.common.bluetooth.fastpair.Constants.TransportDiscoveryService;
+import com.android.server.nearby.common.bluetooth.fastpair.Constants.TransportDiscoveryService.BrHandoverDataCharacteristic;
+import com.android.server.nearby.common.bluetooth.fastpair.Constants.TransportDiscoveryService.ControlPointCharacteristic;
 import com.android.server.nearby.common.bluetooth.fastpair.EllipticCurveDiffieHellmanExchange;
 import com.android.server.nearby.common.bluetooth.fastpair.Ltv;
 import com.android.server.nearby.common.bluetooth.fastpair.MessageStreamHmacEncoder;
@@ -98,13 +108,8 @@
 
 import com.google.common.base.Ascii;
 import com.google.common.primitives.Bytes;
-
-import com.android.server.nearby.common.bloomfilter.BloomFilter;
-import com.android.server.nearby.common.bloomfilter.FastPairBloomFilterHasher;
-
 import com.google.protobuf.ByteString;
 
-import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Method;
 import java.nio.ByteOrder;
 import java.nio.charset.StandardCharsets;
@@ -250,12 +255,14 @@
                         return;
                     }
                     if (isDestroyed) {
-                        // Sometimes this receiver does not successfully unregister in destroy() which causes
-                        // events to occur after the simulator is stopped, so ignore those events.
+                        // Sometimes this receiver does not successfully unregister in destroy()
+                        // which causes events to occur after the simulator is stopped, so ignore
+                        // those events.
                         logger.log("Intent received after simulator destroyed, ignoring");
                         return;
                     }
-                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                    BluetoothDevice device = intent.getParcelableExtra(
+                            BluetoothDevice.EXTRA_DEVICE);
                     switch (intent.getAction()) {
                         case BluetoothAdapter.ACTION_SCAN_MODE_CHANGED:
                             if (isDiscoverable()) {
@@ -263,28 +270,34 @@
                             }
                             break;
                         case BluetoothDevice.ACTION_PAIRING_REQUEST:
-                            int variant = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, ERROR);
+                            int variant = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
+                                    ERROR);
                             int key = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, ERROR);
                             logger.log(
-                                    "Pairing request, variant=%d, key=%s", variant, key == ERROR ? "(none)" : key);
+                                    "Pairing request, variant=%d, key=%s", variant,
+                                    key == ERROR ? "(none)" : key);
 
                             // Prevent Bluetooth Settings from getting the pairing request.
                             abortBroadcast();
 
                             pairingDevice = device;
                             if (secret == null) {
-                                // We haven't done the handshake over GATT to agree on the shared secret. For now,
-                                // just accept anyway (so we can still simulate old 1.0 model IDs).
+                                // We haven't done the handshake over GATT to agree on the shared
+                                // secret. For now, just accept anyway (so we can still simulate
+                                // old 1.0 model IDs).
                                 logger.log("No handshake, auto-accepting anyway.");
                                 setPasskeyConfirmation(true);
-                            } else if (variant == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
-                                // Store the passkey. And check it, since there's a race (see method for why).
-                                // Usually this check is a no-op and we'll get the passkey later over GATT.
+                            } else if (variant
+                                    == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
+                                // Store the passkey. And check it, since there's a race (see
+                                // method for why). Usually this check is a no-op and we'll get
+                                // the passkey later over GATT.
                                 localPasskey = key;
                                 checkPasskey();
                             } else if (variant == PAIRING_VARIANT_DISPLAY_PASSKEY) {
                                 if (passkeyEventCallback != null) {
-                                    passkeyEventCallback.onPasskeyRequested(FastPairSimulator.this::enterPassKey);
+                                    passkeyEventCallback.onPasskeyRequested(
+                                            FastPairSimulator.this::enterPassKey);
                                 } else {
                                     logger.log("passkeyEventCallback is not set!");
                                     enterPassKey(key);
@@ -295,20 +308,22 @@
                             } else if (variant == BluetoothDevice.PAIRING_VARIANT_PIN) {
                                 if (passkeyEventCallback != null) {
                                     passkeyEventCallback.onPasskeyRequested(
-                                            (int pin) ->
-                                                    pairingDevice.setPin(
-                                                            convertPinToBytes(
-                                                                    String.format(Locale.ENGLISH, "%d", pin))));
+                                            (int pin) -> {
+                                                byte[] newPin = convertPinToBytes(
+                                                        String.format(Locale.ENGLISH, "%d", pin));
+                                                pairingDevice.setPin(newPin);
+                                            });
                                 }
                             } else {
-                                // Reject the pairing request if it's not using the Numeric Comparison (aka Passkey
-                                // Confirmation) method.
+                                // Reject the pairing request if it's not using the Numeric
+                                // Comparison (aka Passkey Confirmation) method.
                                 setPasskeyConfirmation(false);
                             }
                             break;
                         case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
                             int bondState =
-                                    intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
+                                    intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+                                            BluetoothDevice.BOND_NONE);
                             logger.log("Bond state to %s changed to %d", device, bondState);
                             switch (bondState) {
                                 case BluetoothDevice.BOND_BONDING:
@@ -323,7 +338,9 @@
                                     setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
 
                                     // If it is subsequent pair, we need to add paired device here.
-                                    if (isSubsequentPair && secret != null && secret.length == AES_BLOCK_LENGTH) {
+                                    if (isSubsequentPair
+                                            && secret != null
+                                            && secret.length == AES_BLOCK_LENGTH) {
                                         addAccountKey(secret, pairingDevice);
                                     }
                                     break;
@@ -357,8 +374,8 @@
                             }
                             break;
                         default:
-                            logger.log(
-                                    new IllegalArgumentException(intent.toString()), "Received unexpected intent");
+                            logger.log(new IllegalArgumentException(intent.toString()),
+                                    "Received unexpected intent");
                             break;
                     }
                 }
@@ -370,12 +387,7 @@
             return null;
         }
         byte[] pinBytes;
-        try {
-            pinBytes = pin.getBytes("UTF-8");
-        } catch (UnsupportedEncodingException uee) {
-            logger.log("UTF-8 not supported?!?");
-            return null;
-        }
+        pinBytes = pin.getBytes(StandardCharsets.UTF_8);
         if (pinBytes.length <= 0 || pinBytes.length > 16) {
             return null;
         }
@@ -395,7 +407,8 @@
                 }
 
                 @Override
-                public void write(BluetoothGattServerConnection connection, int offset, byte[] value) {
+                public void write(
+                        BluetoothGattServerConnection connection, int offset, byte[] value) {
                     logger.log("Got value from passkey servlet: %s", base16().encode(value));
                     if (secret == null) {
                         logger.log("Ignoring write to passkey characteristic, no pairing secret.");
@@ -403,8 +416,8 @@
                     }
 
                     try {
-                        remotePasskey =
-                                PasskeyCharacteristic.decrypt(PasskeyCharacteristic.Type.SEEKER, secret, value);
+                        remotePasskey = PasskeyCharacteristic.decrypt(
+                                PasskeyCharacteristic.Type.SEEKER, secret, value);
                         if (passkeyEventCallback != null) {
                             passkeyEventCallback.onRemotePasskeyReceived(remotePasskey);
                         }
@@ -430,17 +443,18 @@
                 }
 
                 @Override
-                public void write(BluetoothGattServerConnection connection, int offset, byte[] value) {
+                public void write(
+                        BluetoothGattServerConnection connection, int offset, byte[] value) {
                     logger.log("Got value from device naming servlet: %s", base16().encode(value));
                     if (secret == null) {
                         logger.log("Ignoring write to name characteristic, no pairing secret.");
                         return;
                     }
-                    // Parse the device name from seeker to write name into provider. See
-                    // go/fast-pair-naming-design-doc for the decryption detail to get the device name.
+                    // Parse the device name from seeker to write name into provider.
                     logger.log("Got name byte array size = %d", value.length);
                     try {
-                        String decryptedDeviceName = NamingEncoder.decodeNamingPacket(secret, value);
+                        String decryptedDeviceName =
+                                NamingEncoder.decodeNamingPacket(secret, value);
                         if (decryptedDeviceName != null) {
                             setDeviceName(decryptedDeviceName.getBytes(StandardCharsets.UTF_8));
                             logger.log("write device name = %s", decryptedDeviceName);
@@ -458,7 +472,8 @@
 
     private Value bluetoothAddress;
     private final FastPairAdvertiser advertiser;
-    private final Map<String, BluetoothGattServerHelper> bluetoothGattServerHelpers = new HashMap<>();
+    private final Map<String, BluetoothGattServerHelper> mBluetoothGattServerHelpers =
+            new HashMap<>();
     private CountDownLatch isDiscoverableLatch = new CountDownLatch(1);
     private ScheduledFuture<?> revertDiscoverableFuture;
     private boolean shouldFailPairing = false;
@@ -532,9 +547,9 @@
     }
 
     /** Send Event Message on to rfcomm connected devices. */
-    public void sendEventStreamMessageToRfcommDevices(EventStreamProtocol.EventGroup eventGroup) {
+    public void sendEventStreamMessageToRfcommDevices(EventGroup eventGroup) {
         // Send fake log when event code is logging and type is not using Log_Full event.
-        if (eventGroup == EventStreamProtocol.EventGroup.LOGGING && !useLogFullEvent) {
+        if (eventGroup == EventGroup.LOGGING && !useLogFullEvent) {
             rfcommServer.sendFakeEventStreamLoggingMessage(
                     getDeviceName()
                             + " "
@@ -568,8 +583,8 @@
     }
 
     /**
-     * Callback when there comes a passkey input request from BT service, or receiving remote device's
-     * passkey.
+     * Callback when there comes a passkey input request from BT service, or receiving remote
+     * device's passkey.
      */
     public interface PasskeyEventCallback {
         void onPasskeyRequested(KeyInputCallback keyInputCallback);
@@ -816,7 +831,8 @@
                 this.mCallback = option.mCallback;
                 this.mIncludeTransportDataDescriptor = option.mIncludeTransportDataDescriptor;
                 this.mAntiSpoofingPrivateKey = option.mAntiSpoofingPrivateKey;
-                this.mUseRandomSaltForAccountKeyRotation = option.mUseRandomSaltForAccountKeyRotation;
+                this.mUseRandomSaltForAccountKeyRotation =
+                        option.mUseRandomSaltForAccountKeyRotation;
                 this.mIsMemoryTest = option.mIsMemoryTest;
                 this.mBecomeDiscoverable = option.mBecomeDiscoverable;
                 this.mShowsPasskeyConfirmation = option.mShowsPasskeyConfirmation;
@@ -834,7 +850,10 @@
                 return this;
             }
 
-            /** Must be a 6-byte hex string (optionally with colons). Default is this device's BT MAC. */
+            /**
+             * Must be a 6-byte hex string (optionally with colons).
+             * Default is this device's BT MAC.
+             */
             public Builder setBluetoothAddress(@Nullable String bluetoothAddress) {
                 this.mBluetoothAddress = bluetoothAddress;
                 return this;
@@ -864,10 +883,11 @@
 
             /**
              * Set whether to include the Transport Data descriptor, which has the list of supported
-             * profiles. This is required by the spec, but if we can't get it, we recover gracefully by
-             * assuming support for one of {A2DP, Headset}. Default is true.
+             * profiles. This is required by the spec, but if we can't get it, we recover gracefully
+             * by assuming support for one of {A2DP, Headset}. Default is true.
              */
-            public Builder setIncludeTransportDataDescriptor(boolean includeTransportDataDescriptor) {
+            public Builder setIncludeTransportDataDescriptor(
+                    boolean includeTransportDataDescriptor) {
                 this.mIncludeTransportDataDescriptor = includeTransportDataDescriptor;
                 return this;
             }
@@ -915,7 +935,10 @@
                 return this;
             }
 
-            /** Non-public because this is required to create a builder. See {@link Options#builder}. */
+            /**
+             * Non-public because this is required to create a builder. See
+             * {@link Options#builder}.
+             */
             public Builder setModelId(String modelId) {
                 this.mModelId = modelId;
                 return this;
@@ -959,7 +982,8 @@
         String bluetoothAddress =
                 !TextUtils.isEmpty(options.getBluetoothAddress())
                         ? options.getBluetoothAddress()
-                        : Settings.Secure.getString(context.getContentResolver(), "bluetooth_address");
+                        : Settings.Secure.getString(context.getContentResolver(),
+                                "bluetooth_address");
         if (bluetoothAddress == null && VERSION.SDK_INT >= VERSION_CODES.O) {
             // Requires a modified Android O build for access to bluetoothAdapter.getAddress().
             // See http://google3/java/com/google/location/nearby/apps/fastpair/simulator/README.md.
@@ -979,12 +1003,12 @@
                 deviceName != null ? new String(deviceName, StandardCharsets.UTF_8) : null);
 
         if (dataOnlyConnection) {
-            // To get BLE address, we need to start advertising first, and then {@code #setBleAddress}
-            // will be called with BLE address.
+            // To get BLE address, we need to start advertising first, and then
+            // {@code#setBleAddress} will be called with BLE address.
             advertiser.startAdvertising(modelIdServiceData(/* forAdvertising= */ true));
         } else {
-            // TODO(jklinker): Make this so that the simulator doesn't start automatically. This is tricky
-            // since the simulator is used in our integ tests as well.
+            // Make this so that the simulator doesn't start automatically.
+            // This is tricky since the simulator is used in our integ tests as well.
             start(bleAddress != null ? bleAddress : bluetoothAddress);
         }
     }
@@ -1001,7 +1025,7 @@
                 (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
         BluetoothGattServerHelper bluetoothGattServerHelper =
                 new BluetoothGattServerHelper(context, wrap(bluetoothManager));
-        bluetoothGattServerHelpers.put(address, bluetoothGattServerHelper);
+        mBluetoothGattServerHelpers.put(address, bluetoothGattServerHelper);
 
         if (options.getBecomeDiscoverable()) {
             try {
@@ -1018,8 +1042,8 @@
     }
 
     /**
-     * Regenerate service data on a fixed interval. This causes the bloom filter to be refreshed and a
-     * different salt to be used for rotation.
+     * Regenerate service data on a fixed interval.
+     * This causes the bloom filter to be refreshed and a different salt to be used for rotation.
      */
     @SuppressWarnings("FutureReturnValueIgnored")
     private void scheduleAdvertisingRefresh() {
@@ -1044,7 +1068,7 @@
             isDestroyed = true;
             context.unregisterReceiver(broadcastReceiver);
             advertiser.stopAdvertising();
-            for (BluetoothGattServerHelper helper : bluetoothGattServerHelpers.values()) {
+            for (BluetoothGattServerHelper helper : mBluetoothGattServerHelpers.values()) {
                 helper.close();
             }
             stopRfcommServer();
@@ -1088,8 +1112,8 @@
         // When BLE address changes, needs to send BLE address to the client again.
         sendDeviceBleAddress(bleAddress);
 
-        // If we are advertising something other than the model id (eg the bloom filter), restart the
-        // advertisement so that it is updated with the new address.
+        // If we are advertising something other than the model id (e.g. the bloom filter), restart
+        // the advertisement so that it is updated with the new address.
         if (isAdvertising() && !isDiscoverable()) {
             advertiser.startAdvertising(getServiceData());
         }
@@ -1123,7 +1147,8 @@
             characteristic.addDescriptor(
                     new BluetoothGattDescriptor(
                             Constants.CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR_UUID,
-                            BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE));
+                            BluetoothGattDescriptor.PERMISSION_READ
+                                    | BluetoothGattDescriptor.PERMISSION_WRITE));
             return characteristic;
         }
 
@@ -1157,9 +1182,9 @@
                         for (Map.Entry<BluetoothGattServerConnection, Notifier> entry :
                                 connections.entrySet()) {
                             try {
-                                logger.log(
-                                        "Sending notify %s to %s",
-                                        getCharacteristic(), entry.getKey().getDevice().getAddress());
+                                logger.log("Sending notify %s to %s",
+                                        getCharacteristic(),
+                                        entry.getKey().getDevice().getAddress());
                                 entry.getValue().notify(data);
                             } catch (BluetoothException e) {
                                 logger.log(
@@ -1174,60 +1199,63 @@
     }
 
     private void startRfcommServer() {
-        rfcommServer.setRequestHandler(
-                (int eventGroup, int eventCode, byte[] data) -> {
-                    switch (eventGroup) {
-                        case EventStreamProtocol.EventGroup.DEVICE_VALUE:
-                            if (eventCode == EventStreamProtocol.DeviceEventCode.DEVICE_CAPABILITY_VALUE
-                                    && data != null) {
-                                logger.log("Received phone capability: %s", base16().encode(data));
-                            } else if (eventCode == EventStreamProtocol.DeviceEventCode.PLATFORM_TYPE_VALUE
-                                    && data != null) {
-                                logger.log("Received platform type: %s", base16().encode(data));
-                            }
-                            break;
-                        case EventStreamProtocol.EventGroup.DEVICE_ACTION_VALUE:
-                            if (eventCode == EventStreamProtocol.DeviceActionEventCode.DEVICE_ACTION_RING_VALUE) {
-                                logger.log("receive device action with ring value, data = %d", data[0]);
-                                sendDeviceRingActionResponse();
-                                // Simulate notifying the seeker that the ringing has stopped due to user
-                                // interaction (such as tapping the bud).
-                                uiThreadHandler.postDelayed(this::sendDeviceRingStoppedAction, 5000);
-                            }
-                            break;
-                        case EventStreamProtocol.EventGroup.DEVICE_CONFIGURATION_VALUE:
-                            if (eventCode
-                                    == EventStreamProtocol.DeviceConfigurationEventCode.CONFIGURATION_BUFFER_SIZE_VALUE) {
-                                logger.log(
-                                        "receive device action with buffer size value, data = %s",
-                                        base16().encode(data));
-                                sendSetBufferActionResponse(data);
-                            }
-                            break;
-                        case EventStreamProtocol.EventGroup.DEVICE_CAPABILITY_SYNC_VALUE:
-                            if (eventCode
-                                    == EventStreamProtocol.DeviceCapabilitySyncEventCode.REQUEST_CAPABILITY_UPDATE_VALUE) {
-                                logger.log("receive device capability update request.");
-                                sendCapabilitySync();
-                            }
-                            break;
-                        default: // fall out
-                    }
-                });
-        rfcommServer.setStateMonitor(
-                state -> {
-                    logger.log("RfcommServer is in %s state", state);
-                    if (CONNECTED.equals(state)) {
-                        sendModelId();
-                        sendDeviceBleAddress(bleAddress);
-                        sendFirmwareVersion();
-                        sendSessionNonce();
-                    }
-                });
-
+        rfcommServer.setRequestHandler(this::handleRfcommServerRequest);
+        rfcommServer.setStateMonitor(state -> {
+            logger.log("RfcommServer is in %s state", state);
+            if (CONNECTED.equals(state)) {
+                sendModelId();
+                sendDeviceBleAddress(bleAddress);
+                sendFirmwareVersion();
+                sendSessionNonce();
+            }
+        });
         rfcommServer.start();
     }
 
+    private void handleRfcommServerRequest(int eventGroup, int eventCode, byte[] data) {
+        switch (eventGroup) {
+            case EventGroup.DEVICE_VALUE:
+                if (data == null) {
+                    break;
+                }
+
+                String deviceValue = base16().encode(data);
+                if (eventCode == DeviceEventCode.DEVICE_CAPABILITY_VALUE) {
+                    logger.log("Received phone capability: %s", deviceValue);
+                } else if (eventCode == DeviceEventCode.PLATFORM_TYPE_VALUE) {
+                    logger.log("Received platform type: %s", deviceValue);
+                }
+                break;
+            case EventGroup.DEVICE_ACTION_VALUE:
+                if (eventCode == DeviceActionEventCode.DEVICE_ACTION_RING_VALUE) {
+                    logger.log("receive device action with ring value, data = %d",
+                            data[0]);
+                    sendDeviceRingActionResponse();
+                    // Simulate notifying the seeker that the ringing has stopped due
+                    // to user interaction (such as tapping the bud).
+                    uiThreadHandler.postDelayed(this::sendDeviceRingStoppedAction,
+                            5000);
+                }
+                break;
+            case EventGroup.DEVICE_CONFIGURATION_VALUE:
+                if (eventCode == DeviceConfigurationEventCode.CONFIGURATION_BUFFER_SIZE_VALUE) {
+                    logger.log(
+                            "receive device action with buffer size value, data = %s",
+                            base16().encode(data));
+                    sendSetBufferActionResponse(data);
+                }
+                break;
+            case EventGroup.DEVICE_CAPABILITY_SYNC_VALUE:
+                if (eventCode == DeviceCapabilitySyncEventCode.REQUEST_CAPABILITY_UPDATE_VALUE) {
+                    logger.log("receive device capability update request.");
+                    sendCapabilitySync();
+                }
+                break;
+            default: // fall out
+                break;
+        }
+    }
+
     private void stopRfcommServer() {
         rfcommServer.stop();
         rfcommServer.setRequestHandler(null);
@@ -1237,18 +1265,17 @@
     private void sendModelId() {
         logger.log("Send model ID to the client");
         rfcommServer.send(
-                EventStreamProtocol.EventGroup.DEVICE_VALUE,
-                EventStreamProtocol.DeviceEventCode.DEVICE_MODEL_ID_VALUE,
+                EventGroup.DEVICE_VALUE,
+                DeviceEventCode.DEVICE_MODEL_ID_VALUE,
                 modelIdServiceData(/* forAdvertising= */ false));
     }
 
     private void sendDeviceBleAddress(String bleAddress) {
         logger.log("Send BLE address (%s) to the client", bleAddress);
-        // TODO(b/134244147): to solve central address resolution problem, adds api for simulator app.
         if (bleAddress != null) {
             rfcommServer.send(
-                    EventStreamProtocol.EventGroup.DEVICE_VALUE,
-                    EventStreamProtocol.DeviceEventCode.DEVICE_BLE_ADDRESS_VALUE,
+                    EventGroup.DEVICE_VALUE,
+                    DeviceEventCode.DEVICE_BLE_ADDRESS_VALUE,
                     BluetoothAddress.decode(bleAddress));
         }
     }
@@ -1256,8 +1283,8 @@
     private void sendFirmwareVersion() {
         logger.log("Send Firmware Version (%s) to the client", deviceFirmwareVersion);
         rfcommServer.send(
-                EventStreamProtocol.EventGroup.DEVICE_VALUE,
-                EventStreamProtocol.DeviceEventCode.FIRMWARE_VERSION_VALUE,
+                EventGroup.DEVICE_VALUE,
+                DeviceEventCode.FIRMWARE_VERSION_VALUE,
                 deviceFirmwareVersion.getBytes());
     }
 
@@ -1266,18 +1293,18 @@
         SecureRandom secureRandom = new SecureRandom();
         sessionNonce = new byte[SECTION_NONCE_LENGTH];
         secureRandom.nextBytes(sessionNonce);
-        rfcommServer.send(EventStreamProtocol.EventGroup.DEVICE_VALUE,
-                EventStreamProtocol.DeviceEventCode.SECTION_NONCE_VALUE, sessionNonce);
+        rfcommServer.send(
+                EventGroup.DEVICE_VALUE, DeviceEventCode.SECTION_NONCE_VALUE, sessionNonce);
     }
 
     private void sendDeviceRingActionResponse() {
         logger.log("Send device ring action response to the client");
         rfcommServer.send(
-                EventStreamProtocol.EventGroup.ACKNOWLEDGEMENT_VALUE,
-                EventStreamProtocol.AcknowledgementEventCode.ACKNOWLEDGEMENT_ACK_VALUE,
+                EventGroup.ACKNOWLEDGEMENT_VALUE,
+                AcknowledgementEventCode.ACKNOWLEDGEMENT_ACK_VALUE,
                 new byte[]{
-                        EventStreamProtocol.EventGroup.DEVICE_ACTION_VALUE,
-                        EventStreamProtocol.DeviceActionEventCode.DEVICE_ACTION_RING_VALUE
+                        EventGroup.DEVICE_ACTION_VALUE,
+                        DeviceActionEventCode.DEVICE_ACTION_RING_VALUE
                 });
     }
 
@@ -1285,10 +1312,11 @@
         boolean hmacPassed = false;
         for (ByteString accountKey : getAccountKeys()) {
             try {
-                if (MessageStreamHmacEncoder.verifyHmac(accountKey.toByteArray(), sessionNonce, data)) {
+                if (MessageStreamHmacEncoder.verifyHmac(
+                        accountKey.toByteArray(), sessionNonce, data)) {
                     hmacPassed = true;
-                    logger.log(
-                            "Buffer size data matches account key %s", base16().encode(accountKey.toByteArray()));
+                    logger.log("Buffer size data matches account key %s",
+                            base16().encode(accountKey.toByteArray()));
                     break;
                 }
             } catch (GeneralSecurityException e) {
@@ -1298,11 +1326,11 @@
         if (hmacPassed) {
             logger.log("Send buffer size action response %s to the client", base16().encode(data));
             rfcommServer.send(
-                    EventStreamProtocol.EventGroup.ACKNOWLEDGEMENT_VALUE,
-                    EventStreamProtocol.AcknowledgementEventCode.ACKNOWLEDGEMENT_ACK_VALUE,
+                    EventGroup.ACKNOWLEDGEMENT_VALUE,
+                    AcknowledgementEventCode.ACKNOWLEDGEMENT_ACK_VALUE,
                     new byte[]{
-                            EventStreamProtocol.EventGroup.DEVICE_CONFIGURATION_VALUE,
-                            EventStreamProtocol.DeviceConfigurationEventCode.CONFIGURATION_BUFFER_SIZE_VALUE,
+                            EventGroup.DEVICE_CONFIGURATION_VALUE,
+                            DeviceConfigurationEventCode.CONFIGURATION_BUFFER_SIZE_VALUE,
                             data[0],
                             data[1],
                             data[2]
@@ -1317,8 +1345,8 @@
         if (supportDynamicBufferSize) {
             logger.log("Send dynamic buffer size range to the client");
             rfcommServer.send(
-                    EventStreamProtocol.EventGroup.DEVICE_CAPABILITY_SYNC_VALUE,
-                    EventStreamProtocol.DeviceCapabilitySyncEventCode.CONFIGURABLE_BUFFER_SIZE_RANGE_VALUE,
+                    EventGroup.DEVICE_CAPABILITY_SYNC_VALUE,
+                    DeviceCapabilitySyncEventCode.CONFIGURABLE_BUFFER_SIZE_RANGE_VALUE,
                     new byte[]{
                             0x00, 0x01, (byte) 0xf4, 0x00, 0x64, 0x00, (byte) 0xc8,
                             0x01, 0x00, (byte) 0xff, 0x00, 0x01, 0x00, (byte) 0x88,
@@ -1332,8 +1360,8 @@
     private void sendDeviceRingStoppedAction() {
         logger.log("Sending device ring stopped action to the client");
         rfcommServer.send(
-                EventStreamProtocol.EventGroup.DEVICE_ACTION_VALUE,
-                EventStreamProtocol.DeviceActionEventCode.DEVICE_ACTION_RING_VALUE,
+                EventGroup.DEVICE_ACTION_VALUE,
+                DeviceActionEventCode.DEVICE_ACTION_RING_VALUE,
                 // Additional data for stopping ringing on all components.
                 new byte[]{0x00});
     }
@@ -1343,16 +1371,16 @@
                 new NotifiableGattServlet() {
                     @Override
                     public BluetoothGattCharacteristic getBaseCharacteristic() {
-                        return new BluetoothGattCharacteristic(
-                                TransportDiscoveryService.ControlPointCharacteristic.ID,
-                                PROPERTY_WRITE | PROPERTY_INDICATE,
-                                PERMISSION_WRITE);
+                        return new BluetoothGattCharacteristic(ControlPointCharacteristic.ID,
+                                PROPERTY_WRITE | PROPERTY_INDICATE, PERMISSION_WRITE);
                     }
 
                     @Override
-                    public void write(BluetoothGattServerConnection connection, int offset, byte[] value)
+                    public void write(
+                            BluetoothGattServerConnection connection, int offset, byte[] value)
                             throws BluetoothGattException {
-                        logger.log("Requested TDS Control Point write, value=%s", base16().encode(value));
+                        logger.log("Requested TDS Control Point write, value=%s",
+                                base16().encode(value));
 
                         ResultCode resultCode = checkTdsControlPointRequest(value);
                         if (resultCode == ResultCode.SUCCESS) {
@@ -1369,7 +1397,10 @@
                         logger.log("Sending TDS Control Point response indication");
                         sendNotification(
                                 Bytes.concat(
-                                        new byte[]{getTdsControlPointOpCode(value), resultCode.byteValue},
+                                        new byte[]{
+                                                getTdsControlPointOpCode(value),
+                                                resultCode.byteValue,
+                                        },
                                         resultCode == ResultCode.SUCCESS
                                                 ? TDS_CONTROL_POINT_RESPONSE_PARAMETER
                                                 : new byte[0]));
@@ -1381,17 +1412,14 @@
 
                     @Override
                     public BluetoothGattCharacteristic getCharacteristic() {
-                        return new BluetoothGattCharacteristic(
-                                TransportDiscoveryService.BrHandoverDataCharacteristic.ID,
-                                PROPERTY_READ,
-                                PERMISSION_READ);
+                        return new BluetoothGattCharacteristic(BrHandoverDataCharacteristic.ID,
+                                PROPERTY_READ, PERMISSION_READ);
                     }
 
                     @Override
-                    public byte[] read(BluetoothGattServerConnection connection, int offset)
-                            throws BluetoothGattException {
+                    public byte[] read(BluetoothGattServerConnection connection, int offset) {
                         return Bytes.concat(
-                                new byte[]{TransportDiscoveryService.BrHandoverDataCharacteristic.BR_EDR_FEATURES},
+                                new byte[]{BrHandoverDataCharacteristic.BR_EDR_FEATURES},
                                 bluetoothAddress.getBytes(ByteOrder.LITTLE_ENDIAN),
                                 CLASS_OF_DEVICE.getBytes(ByteOrder.LITTLE_ENDIAN));
                     }
@@ -1441,14 +1469,18 @@
                     }
 
                     @Override
-                    public void write(BluetoothGattServerConnection connection, int offset, byte[] value) {
-                        logger.log("Got value from account key servlet: %s", base16().encode(value));
+                    public void write(
+                            BluetoothGattServerConnection connection, int offset, byte[] value) {
+                        logger.log("Got value from account key servlet: %s",
+                                base16().encode(value));
                         try {
-                            addAccountKey(AesEcbSingleBlockEncryption.decrypt(secret, value), pairingDevice);
+                            addAccountKey(AesEcbSingleBlockEncryption.decrypt(secret, value),
+                                    pairingDevice);
                         } catch (GeneralSecurityException e) {
                             logger.log(e, "Failed to decrypt account key.");
                         }
-                        uiThreadHandler.post(() -> advertiser.startAdvertising(accountKeysServiceData()));
+                        uiThreadHandler.post(
+                                () -> advertiser.startAdvertising(accountKeysServiceData()));
                     }
                 };
 
@@ -1469,7 +1501,8 @@
         BluetoothGattServlet keyBasedPairingServlet =
                 new NotifiableGattServlet() {
                     @Override
-                    // Simulating deprecated API {@code KeyBasedPairingCharacteristic.ID} for testing.
+                    // Simulating deprecated API {@code KeyBasedPairingCharacteristic.ID} for
+                    // testing.
                     @SuppressWarnings("deprecation")
                     public BluetoothGattCharacteristic getBaseCharacteristic() {
                         return new BluetoothGattCharacteristic(
@@ -1479,9 +1512,10 @@
                     }
 
                     @Override
-                    public void write(BluetoothGattServerConnection connection, int offset, byte[] value)
-                            throws BluetoothGattException {
-                        logger.log("Requesting key based pairing handshake, value=%s", base16().encode(value));
+                    public void write(
+                            BluetoothGattServerConnection connection, int offset, byte[] value) {
+                        logger.log("Requesting key based pairing handshake, value=%s",
+                                base16().encode(value));
 
                         secret = null;
                         byte[] seekerPublicAddress = null;
@@ -1495,18 +1529,19 @@
                                     isSubsequentPair = true;
                                     break;
                                 } catch (GeneralSecurityException e) {
-                                    logger.log(e, "Failed to decrypt with %s", base16().encode(candidateSecret));
+                                    logger.log(e, "Failed to decrypt with %s",
+                                            base16().encode(candidateSecret));
                                 }
                             }
-                        } else if (value.length
-                                == AES_BLOCK_LENGTH + EllipticCurveDiffieHellmanExchange.PUBLIC_KEY_LENGTH
+                        } else if (value.length == AES_BLOCK_LENGTH + PUBLIC_KEY_LENGTH
                                 && options.getAntiSpoofingPrivateKey() != null) {
                             try {
                                 byte[] encryptedRequest = Arrays.copyOf(value, AES_BLOCK_LENGTH);
                                 byte[] receivedPublicKey =
                                         Arrays.copyOfRange(value, AES_BLOCK_LENGTH, value.length);
                                 byte[] candidateSecret =
-                                        EllipticCurveDiffieHellmanExchange.create(options.getAntiSpoofingPrivateKey())
+                                        EllipticCurveDiffieHellmanExchange.create(
+                                                        options.getAntiSpoofingPrivateKey())
                                                 .generateSecret(receivedPublicKey);
                                 seekerPublicAddress = handshake(candidateSecret, encryptedRequest);
                                 secret = candidateSecret;
@@ -1530,54 +1565,61 @@
                         byte[] salt = new byte[9];
                         new Random().nextBytes(salt);
                         try {
-                            byte[] encryptedAddress =
-                                    encrypt(
-                                            secret,
-                                            Bytes.concat(
-                                                    new byte[]{KeyBasedPairingCharacteristic.Response.TYPE},
-                                                    bluetoothAddress.getBytes(ByteOrder.BIG_ENDIAN),
-                                                    salt));
+                            byte[] data = concat(
+                                    new byte[]{KeyBasedPairingCharacteristic.Response.TYPE},
+                                    bluetoothAddress.getBytes(ByteOrder.BIG_ENDIAN), salt);
+                            byte[] encryptedAddress = encrypt(secret, data);
                             logger.log(
                                     "Sending handshake response %s with size %d",
                                     base16().encode(encryptedAddress), encryptedAddress.length);
                             sendNotification(encryptedAddress);
 
-                            // Notify seeker for NameCharacteristic to get provider device name  when seeker
-                            // request device name flag is true.
-                            if (options.getEnableNameCharacteristic() && handshakeRequest.requestDeviceName()) {
+                            // Notify seeker for NameCharacteristic to get provider device name
+                            // when seeker request device name flag is true.
+                            if (options.getEnableNameCharacteristic()
+                                    && handshakeRequest.requestDeviceName()) {
                                 byte[] encryptedResponse =
-                                        getDeviceNameInBytes() != null ? createEncryptedDeviceName() : new byte[0];
+                                        getDeviceNameInBytes() != null ? createEncryptedDeviceName()
+                                                : new byte[0];
                                 logger.log(
                                         "Sending device name response %s with size %d",
-                                        base16().encode(encryptedResponse), encryptedResponse.length);
+                                        base16().encode(encryptedResponse),
+                                        encryptedResponse.length);
                                 deviceNameServlet.sendNotification(encryptedResponse);
                             }
 
-                            // Disconnects the current connection to allow the following pairing request.
-                            // Needs to be on a separate thread to avoid deadlocking and timing out (waits for a
-                            // callback from OS, which happens on this thread).
+                            // Disconnects the current connection to allow the following pairing
+                            // request. Needs to be on a separate thread to avoid deadlocking and
+                            // timing out (waits for a callback from OS, which happens on this
+                            // thread).
                             //
-                            // Note: The spec does not require you to disconnect from other devices at this point.
-                            // If headphones support multiple simultaneous connections, they should stay
-                            // connected. But Android fails to pair with the new device if we don't first
-                            // disconnect from any other device.
-                            logger.log("Skip remove bond, value=%s", options.getRemoveAllDevicesDuringPairing());
+                            // Note: The spec does not require you to disconnect from other
+                            // devices at this point.
+                            // If headphones support multiple simultaneous connections, they
+                            // should stay connected. But Android fails to pair with the new
+                            // device if we don't first disconnect from any other device.
+                            logger.log("Skip remove bond, value=%s",
+                                    options.getRemoveAllDevicesDuringPairing());
                             if (options.getRemoveAllDevicesDuringPairing()
-                                    && handshakeRequest.getType() == HandshakeRequest.Type.KEY_BASED_PAIRING_REQUEST
+                                    && handshakeRequest.getType()
+                                    == HandshakeRequest.Type.KEY_BASED_PAIRING_REQUEST
                                     && !handshakeRequest.requestRetroactivePair()) {
                                 executor.execute(() -> disconnect());
                             }
 
-                            if (handshakeRequest.getType() == HandshakeRequest.Type.KEY_BASED_PAIRING_REQUEST
+                            if (handshakeRequest.getType()
+                                    == HandshakeRequest.Type.KEY_BASED_PAIRING_REQUEST
                                     && handshakeRequest.requestProviderInitialBonding()) {
-                                // Run on executor to ensure it doesn't happen until after the notify (which tells
-                                // the remote device what address to expect).
-                                String seekerPublicAddressString = BluetoothAddress.encode(seekerPublicAddress);
-                                executor.execute(
-                                        () -> {
-                                            logger.log("Sending pairing request to %s", seekerPublicAddressString);
-                                            bluetoothAdapter.getRemoteDevice(seekerPublicAddressString).createBond();
-                                        });
+                                // Run on executor to ensure it doesn't happen until after the
+                                // notify (which tells the remote device what address to expect).
+                                String seekerPublicAddressString =
+                                        BluetoothAddress.encode(seekerPublicAddress);
+                                executor.execute(() -> {
+                                    logger.log("Sending pairing request to %s",
+                                            seekerPublicAddressString);
+                                    bluetoothAdapter.getRemoteDevice(
+                                            seekerPublicAddressString).createBond();
+                                });
                             }
                         } catch (GeneralSecurityException e) {
                             logger.log(e, "Failed to notify of static mac address");
@@ -1591,9 +1633,10 @@
 
                         byte[] decryptedAddress = handshakeRequest.getVerificationData();
                         if (bleAddress != null
-                                && Arrays.equals(decryptedAddress, BluetoothAddress.decode(bleAddress))
-                                || (Arrays.equals(
-                                decryptedAddress, bluetoothAddress.getBytes(ByteOrder.BIG_ENDIAN)))) {
+                                && Arrays.equals(decryptedAddress,
+                                BluetoothAddress.decode(bleAddress))
+                                || Arrays.equals(decryptedAddress,
+                                bluetoothAddress.getBytes(ByteOrder.BIG_ENDIAN))) {
                             logger.log("Address matches: %s", base16().encode(decryptedAddress));
                         } else {
                             throw new GeneralSecurityException(
@@ -1626,7 +1669,8 @@
                         }
 
                         logger.log(
-                                "KeyBasedPairing: initialBonding=%s, requestDeviceName=%s, retroactivePair=%s",
+                                "KeyBasedPairing: initialBonding=%s, requestDeviceName=%s, "
+                                        + "retroactivePair=%s",
                                 handshakeRequest.requestProviderInitialBonding(),
                                 handshakeRequest.requestDeviceName(),
                                 handshakeRequest.requestRetroactivePair());
@@ -1659,7 +1703,8 @@
                             logger.log("Requesting action over BLE, device action");
                         } else if (handshakeRequest.requestFollowedByAdditionalData()) {
                             logger.log(
-                                    "Requesting action over BLE, followed by additional data, type:%s",
+                                    "Requesting action over BLE, followed by additional data, "
+                                            + "type:%s",
                                     handshakeRequest.getAdditionalDataType());
                         } else {
                             logger.log("Requesting action over BLE");
@@ -1668,8 +1713,7 @@
                     }
 
                     /**
-                     * @return The encrypted device name from provider for seeker to use. See
-                     *     go/fast-pair-naming-design-doc for the encryption detail to encrypt device name.
+                     * @return The encrypted device name from provider for seeker to use.
                      */
                     private byte[] createEncryptedDeviceName() throws GeneralSecurityException {
                         byte[] deviceName = getDeviceNameInBytes();
@@ -1703,7 +1747,8 @@
                             sha256 = MessageDigest.getInstance("SHA-256");
                             sha256.reset();
                         } catch (NoSuchAlgorithmException e) {
-                            throw new IllegalStateException("System missing SHA-256 implementation.", e);
+                            throw new IllegalStateException(
+                                    "System missing SHA-256 implementation.", e);
                         }
                     }
 
@@ -1725,12 +1770,15 @@
                     }
 
                     @Override
-                    public void write(BluetoothGattServerConnection connection, int offset, byte[] value)
+                    public void write(
+                            BluetoothGattServerConnection connection, int offset, byte[] value)
                             throws BluetoothGattException {
-                        logger.log("Got value from beacon actions servlet: %s", base16().encode(value));
+                        logger.log("Got value from beacon actions servlet: %s",
+                                base16().encode(value));
                         if (value.length == 0) {
                             logger.log("Packet length invalid, %d", value.length);
-                            throw new BluetoothGattException("Packet length invalid", GATT_ERROR_INVALID_VALUE);
+                            throw new BluetoothGattException("Packet length invalid",
+                                    GATT_ERROR_INVALID_VALUE);
                         }
                         switch (value[0]) {
                             case BeaconActionType.READ_BEACON_PARAMETERS:
@@ -1747,10 +1795,12 @@
                             case BeaconActionType.RING:
                             case BeaconActionType.READ_RINGING_STATE:
                                 throw new BluetoothGattException(
-                                        "Unimplemented beacon action", BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED);
+                                        "Unimplemented beacon action",
+                                        BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED);
                             default:
                                 throw new BluetoothGattException(
-                                        "Unknown beacon action", BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED);
+                                        "Unknown beacon action",
+                                        BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED);
                         }
                     }
 
@@ -1758,7 +1808,8 @@
                             throws BluetoothGattException {
                         if (value.length < ONE_TIME_AUTH_KEY_LENGTH + ONE_TIME_AUTH_KEY_OFFSET) {
                             logger.log("Packet length invalid, %d", value.length);
-                            throw new BluetoothGattException("Packet length invalid", GATT_ERROR_INVALID_VALUE);
+                            throw new BluetoothGattException(
+                                    "Packet length invalid", GATT_ERROR_INVALID_VALUE);
                         }
                         byte[] hashedAccountKey =
                                 Arrays.copyOfRange(
@@ -1766,7 +1817,8 @@
                                         ONE_TIME_AUTH_KEY_OFFSET,
                                         ONE_TIME_AUTH_KEY_LENGTH + ONE_TIME_AUTH_KEY_OFFSET);
                         if (lastNonce == null) {
-                            throw new BluetoothGattException("Nonce wasn't set", GATT_ERROR_UNAUTHENTICATED);
+                            throw new BluetoothGattException(
+                                    "Nonce wasn't set", GATT_ERROR_UNAUTHENTICATED);
                         }
                         if (ownerOnly) {
                             ByteString accountKey = getOwnerAccountKey();
@@ -1774,7 +1826,8 @@
                                 sha256.update(accountKey.toByteArray());
                                 sha256.update(lastNonce);
                                 return Arrays.equals(
-                                        hashedAccountKey, Arrays.copyOf(sha256.digest(), ONE_TIME_AUTH_KEY_LENGTH));
+                                        hashedAccountKey,
+                                        Arrays.copyOf(sha256.digest(), ONE_TIME_AUTH_KEY_LENGTH));
                             }
                         } else {
                             Set<ByteString> accountKeys = getAccountKeys();
@@ -1782,7 +1835,8 @@
                                 sha256.update(accountKey.toByteArray());
                                 sha256.update(lastNonce);
                                 if (Arrays.equals(
-                                        hashedAccountKey, Arrays.copyOf(sha256.digest(), ONE_TIME_AUTH_KEY_LENGTH))) {
+                                        hashedAccountKey,
+                                        Arrays.copyOf(sha256.digest(), ONE_TIME_AUTH_KEY_LENGTH))) {
                                     return true;
                                 }
                             }
@@ -1807,24 +1861,29 @@
                         return data;
                     }
 
-                    private void handleReadBeaconParameters(byte[] value) throws BluetoothGattException {
+                    private void handleReadBeaconParameters(byte[] value)
+                            throws BluetoothGattException {
                         if (!verifyAccountKeyToken(value, /* ownerOnly= */ false)) {
                             throw new BluetoothGattException(
-                                    "failed to authenticate account key", GATT_ERROR_UNAUTHENTICATED);
+                                    "failed to authenticate account key",
+                                    GATT_ERROR_UNAUTHENTICATED);
                         }
                         sendNotification(
                                 fromBytes(
                                         (byte) BeaconActionType.READ_BEACON_PARAMETERS,
                                         (byte) 5 /* data length */,
                                         TRANSMISSION_POWER)
-                                        .concat(ByteString.copyFrom(intToByteArray(getBeaconClock())))
+                                        .concat(ByteString.copyFrom(
+                                                intToByteArray(getBeaconClock())))
                                         .toByteArray());
                     }
 
-                    private void handleReadProvisioningState(byte[] value) throws BluetoothGattException {
+                    private void handleReadProvisioningState(byte[] value)
+                            throws BluetoothGattException {
                         if (!verifyAccountKeyToken(value, /* ownerOnly= */ false)) {
                             throw new BluetoothGattException(
-                                    "failed to authenticate account key", GATT_ERROR_UNAUTHENTICATED);
+                                    "failed to authenticate account key",
+                                    GATT_ERROR_UNAUTHENTICATED);
                         }
                         byte flags = 0;
                         if (verifyAccountKeyToken(value, /* ownerOnly= */ true)) {
@@ -1846,30 +1905,36 @@
                                             flags)
                                             .concat(
                                                     E2eeCalculator.computeE2eeEid(
-                                                            identityKey, /* exponent= */ 10, getBeaconClock()))
+                                                            identityKey, /* exponent= */ 10,
+                                                            getBeaconClock()))
                                             .toByteArray());
                         }
                     }
 
-                    private void handleSetEphemeralIdentityKey(byte[] value) throws BluetoothGattException {
+                    private void handleSetEphemeralIdentityKey(byte[] value)
+                            throws BluetoothGattException {
                         if (!verifyAccountKeyToken(value, /* ownerOnly= */ true)) {
                             throw new BluetoothGattException(
-                                    "failed to authenticate owner account key", GATT_ERROR_UNAUTHENTICATED);
+                                    "failed to authenticate owner account key",
+                                    GATT_ERROR_UNAUTHENTICATED);
                         }
                         if (value.length
-                                != ONE_TIME_AUTH_KEY_LENGTH + ONE_TIME_AUTH_KEY_OFFSET + IDENTITY_KEY_LENGTH) {
+                                != ONE_TIME_AUTH_KEY_LENGTH + ONE_TIME_AUTH_KEY_OFFSET
+                                + IDENTITY_KEY_LENGTH) {
                             logger.log("Packet length invalid, %d", value.length);
-                            throw new BluetoothGattException("Packet length invalid", GATT_ERROR_INVALID_VALUE);
+                            throw new BluetoothGattException("Packet length invalid",
+                                    GATT_ERROR_INVALID_VALUE);
                         }
                         if (identityKey != null) {
                             throw new BluetoothGattException(
-                                    "Device is already provisioned as Eddystone", GATT_ERROR_UNAUTHENTICATED);
+                                    "Device is already provisioned as Eddystone",
+                                    GATT_ERROR_UNAUTHENTICATED);
                         }
-                        identityKey =
-                                Crypto.aesEcbNoPaddingDecrypt(
-                                        ByteString.copyFrom(ownerAccountKey),
-                                        ByteString.copyFrom(value)
-                                                .substring(ONE_TIME_AUTH_KEY_LENGTH + ONE_TIME_AUTH_KEY_OFFSET));
+                        identityKey = Crypto.aesEcbNoPaddingDecrypt(
+                                ByteString.copyFrom(ownerAccountKey),
+                                ByteString.copyFrom(value)
+                                        .substring(ONE_TIME_AUTH_KEY_LENGTH
+                                                + ONE_TIME_AUTH_KEY_OFFSET));
                     }
                 };
 
@@ -1916,7 +1981,8 @@
         logger.log("enterPassKey called with passkey %d.", passkey);
         try {
             boolean result =
-                    (Boolean) Reflect.on(pairingDevice).withMethod("setPasskey", int.class).get(passkey);
+                    (Boolean) Reflect.on(pairingDevice).withMethod("setPasskey", int.class).get(
+                            passkey);
             logger.log("enterPassKey called with result %b", result);
         } catch (ReflectionException e) {
             logger.log("enterPassKey meet Exception %s.", e.getMessage());
@@ -1938,7 +2004,8 @@
 
         logger.log("Checking localPasskey %s == remotePasskey %s", localPasskey, remotePasskey);
         boolean passkeysMatched = localPasskey == remotePasskey;
-        if (options.getShowsPasskeyConfirmation() && passkeysMatched && passkeyEventCallback != null) {
+        if (options.getShowsPasskeyConfirmation() && passkeysMatched
+                && passkeyEventCallback != null) {
             logger.log("callbacks the UI for passkey confirmation.");
             passkeyEventCallback.onPasskeyConfirmation(localPasskey, this::setPasskeyConfirmation);
         } else {
@@ -1949,7 +2016,8 @@
     private void sendPasskeyToRemoteDevice(int passkey) {
         try {
             passkeyServlet.sendNotification(
-                    PasskeyCharacteristic.encrypt(PasskeyCharacteristic.Type.PROVIDER, secret, passkey));
+                    PasskeyCharacteristic.encrypt(
+                            PasskeyCharacteristic.Type.PROVIDER, secret, passkey));
         } catch (GeneralSecurityException e) {
             logger.log(e, "Failed to encrypt passkey response.");
         }
@@ -1996,7 +2064,8 @@
         setDiscoverable(false);
     }
 
-    private void setDiscoverable(boolean discoverable) throws InterruptedException, TimeoutException {
+    private void setDiscoverable(boolean discoverable)
+            throws InterruptedException, TimeoutException {
         isDiscoverableLatch = new CountDownLatch(1);
         setScanMode(discoverable ? SCAN_MODE_CONNECTABLE_DISCOVERABLE : SCAN_MODE_CONNECTABLE);
         // If we're already discoverable, count down the latch right away. Otherwise,
@@ -2049,11 +2118,12 @@
     private ResultCode checkTdsControlPointRequest(byte[] request) {
         if (request.length < 2) {
             logger.log(
-                    new IllegalArgumentException(), "Expected length >= 2 for %s", base16().encode(request));
+                    new IllegalArgumentException(), "Expected length >= 2 for %s",
+                    base16().encode(request));
             return ResultCode.INVALID_PARAMETER;
         }
         byte opCode = getTdsControlPointOpCode(request);
-        if (opCode != TransportDiscoveryService.ControlPointCharacteristic.ACTIVATE_TRANSPORT_OP_CODE) {
+        if (opCode != ControlPointCharacteristic.ACTIVATE_TRANSPORT_OP_CODE) {
             logger.log(
                     new IllegalArgumentException(),
                     "Expected Activate Transport op code (0x01), got %d",
@@ -2067,7 +2137,6 @@
                     request[1]);
             return ResultCode.UNSUPPORTED_ORGANIZATION_ID;
         }
-        // TODO(jfarfel): Parse out the requested service UUIDs, and if they don't include A2DP, fail.
         return ResultCode.SUCCESS;
     }
 
@@ -2082,10 +2151,11 @@
     private byte[] modelIdServiceData(boolean forAdvertising) {
         // Note: This used to be little-endian but is now big-endian. See b/78229467 for details.
         byte[] modelIdPacket =
-                base16().decode(forAdvertising ? options.getAdvertisingModelId() : options.getModelId());
+                base16().decode(
+                        forAdvertising ? options.getAdvertisingModelId() : options.getModelId());
         if (!batteryValues.isEmpty()) {
-            // If we are going to advertise battery values with the packet, then switch to the non-3-byte
-            // model ID format from go/fast-pair-service-data.
+            // If we are going to advertise battery values with the packet, then switch to the
+            // non-3-byte model ID format.
             modelIdPacket = concat(new byte[]{0b00000110}, modelIdPacket);
         }
         return modelIdPacket;
@@ -2115,15 +2185,15 @@
         }
         BloomFilter bloomFilter =
                 new BloomFilter(
-                        new byte[(int) (1.2 * accountKeys.size()) + 3], new FastPairBloomFilterHasher());
+                        new byte[(int) (1.2 * accountKeys.size()) + 3],
+                        new FastPairBloomFilterHasher());
         String address = bleAddress == null ? SIMULATOR_FAKE_BLE_ADDRESS : bleAddress;
 
-        // Simulator supports Central Address Resolution characteristic, so when paired, the BLE address
-        // in Seeker will be resolved to BR/EDR address. This caused Seeker fails on checking the bloom
-        // filter due to different address is used for salting. In order to let battery values
-        // notification be shown on paired device, we use random salt to workaround it.
-        // TODO(tonyysliu): Remove this workaround when simulator does not support Central Address
-        // Resolution characteristic.
+        // Simulator supports Central Address Resolution characteristic, so when paired, the BLE
+        // address in Seeker will be resolved to BR/EDR address. This caused Seeker fails on
+        // checking the bloom filter due to different address is used for salting. In order to
+        // let battery values notification be shown on paired device, we use random salt to
+        // workaround it.
         boolean advertisingBatteryValues = !batteryValues.isEmpty();
         byte[] salt;
         if (options.getUseRandomSaltForAccountKeyRotation() || advertisingBatteryValues) {
@@ -2157,9 +2227,10 @@
     }
 
     /**
-     * Creates a new field for the packet. The header is formatted 0xLLLLTTTT where LLLL is the length
-     * of the field and TTTT is the type (0 for bloom filter, 1 for salt). See go/fast-pair-2-spec for
-     * more information.
+     * Creates a new field for the packet.
+     *
+     * The header is formatted 0xLLLLTTTT where LLLL is the
+     * length of the field and TTTT is the type (0 for bloom filter, 1 for salt).
      */
     private byte[] createField(byte header, byte[] value) {
         return concat(new byte[]{header}, value);
@@ -2188,13 +2259,13 @@
     }
 
     private byte[] generateBatteryData() {
-        // Byte 0: Battery length and type, first 4 bits are the number of battery values, second 4 are
-        // the type. See go/fast-pair-2-service-data for the battery type definitions.
+        // Byte 0: Battery length and type, first 4 bits are the number of battery values, second
+        // 4 are the type.
         // Byte 1 - length: Battery values, the first bit is charging status, the remaining bits are
         // the actual value between 0 and 100, or -1 for unknown.
         byte[] batteryData = new byte[batteryValues.size() + 1];
-        batteryData[0] =
-                (byte) (batteryValues.size() << 4 | (suppressBatteryNotification ? 0b0100 : 0b0011));
+        batteryData[0] = (byte) (batteryValues.size() << 4
+                | (suppressBatteryNotification ? 0b0100 : 0b0011));
 
         int batteryValueIndex = 1;
         for (BatteryValue batteryValue : batteryValues) {
@@ -2208,13 +2279,12 @@
     }
 
     private byte[] generateAccountKeyData(BloomFilter bloomFilter) {
-        // Byte 0: length and type, first 4 bits are the length of bloom filter, second 4 are the type
-        // which inditcating the subsequent pairing notification is suppressed or not.
+        // Byte 0: length and type, first 4 bits are the length of bloom filter, second 4 are the
+        // type which indicating the subsequent pairing notification is suppressed or not.
         // The following bytes are the data of bloom filter.
         byte[] filterBytes = bloomFilter.asBytes();
-        byte lengthAndType =
-                (byte)
-                        (filterBytes.length << 4 | (suppressSubsequentPairingNotification ? 0b0010 : 0b0000));
+        byte lengthAndType = (byte) (filterBytes.length << 4
+                | (suppressSubsequentPairingNotification ? 0b0010 : 0b0000));
         logger.log(
                 "Generate bloom filter with suppress subsequent pairing notification:%b",
                 suppressSubsequentPairingNotification);
diff --git a/nearby/tests/multidevices/host/Android.bp b/nearby/tests/multidevices/host/Android.bp
index 5aace96..b0adfba 100644
--- a/nearby/tests/multidevices/host/Android.bp
+++ b/nearby/tests/multidevices/host/Android.bp
@@ -16,7 +16,7 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-// Run the tests: atest -v CtsSeekerDiscoverProviderTest -- --replicate-parent-setup --multi-device-count 2
+// Run the tests: atest -v CtsSeekerDiscoverProviderTest
 python_test_host {
     name: "CtsSeekerDiscoverProviderTest",
     main: "seeker_discover_provider_test.py",
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));
-
     }
 }