Merge "Add integration test case for Settings.Secure.FAST_PAIR_SCAN_ENABLED."
diff --git a/nearby/service/java/com/android/server/nearby/NearbyServiceImpl.java b/nearby/service/java/com/android/server/nearby/NearbyServiceImpl.java
index 59ecae1..04e73a5 100644
--- a/nearby/service/java/com/android/server/nearby/NearbyServiceImpl.java
+++ b/nearby/service/java/com/android/server/nearby/NearbyServiceImpl.java
@@ -50,7 +50,7 @@
                     // Have to do this logic in listener. Even during PHASE_BOOT_COMPLETED
                     // phase, BluetoothAdapter is not null, the BleScanner is null.
                     Log.v(TAG, "Initiating BluetoothAdapter when Bluetooth is turned on.");
-                    mSystemInjector.onBluetoothReady();
+                    mSystemInjector.initializeBluetoothAdapter();
                 }
             }
         }
@@ -74,6 +74,7 @@
     }
 
     void onSystemReady() {
+        mSystemInjector.initializeBluetoothAdapter();
         mContext.registerReceiver(mBluetoothReceiver,
                 new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
     }
@@ -93,7 +94,7 @@
             return mBluetoothAdapter;
         }
 
-        synchronized void onBluetoothReady() {
+        synchronized void initializeBluetoothAdapter() {
             if (mBluetoothAdapter != null) {
                 return;
             }
@@ -104,4 +105,5 @@
             mBluetoothAdapter = manager.getAdapter();
         }
     }
+
 }
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
new file mode 100644
index 0000000..a41d7a3
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.fastpair;
+
+import android.content.Context;
+import android.nearby.FastPairDevice;
+import android.nearby.NearbyDevice;
+import android.util.Log;
+
+import com.android.server.nearby.common.locator.Locator;
+import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager;
+import com.android.server.nearby.util.FastPairDecoder;
+import com.android.server.nearby.util.Hex;
+
+import com.google.protobuf.ByteString;
+
+import service.proto.Cache;
+
+/**
+ * Handler that handle fast pair related broadcast.
+ */
+public class FastPairAdvHandler {
+    Context mContext;
+    String mBleAddress;
+
+    /**
+     * Constructor function.
+     */
+    public FastPairAdvHandler(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Handles all of the scanner result. Fast Pair will handle model id broadcast bloomfilter
+     * broadcast and battery level broadcast.
+     */
+    public void handleBroadcast(NearbyDevice device) {
+        FastPairDevice fastPairDevice = (FastPairDevice) device;
+        if (mBleAddress != null && mBleAddress.equals(fastPairDevice.getBluetoothAddress())) {
+            return;
+        }
+        mBleAddress = fastPairDevice.getBluetoothAddress();
+        byte[] model = FastPairDecoder.getModelId(fastPairDevice.getData());
+        Log.d("FastPairService",
+                "On discovery model id" + Hex.bytesToStringLowercase(model));
+        // Use api to get anti spoofing key from model id.
+        Locator.get(mContext, FastPairHalfSheetManager.class).showHalfSheet(
+                Cache.ScanFastPairStoreItem.newBuilder()
+                        .setAddress(mBleAddress)
+                        .setAntiSpoofingPublicKey(ByteString.EMPTY)
+                        .build());
+    }
+}
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 d6625a4..253e942 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
@@ -16,28 +16,20 @@
 
 package com.android.server.nearby.fastpair;
 
-import static com.android.server.nearby.fastpair.Constant.DEVICE_PAIRING_FRAGMENT_TYPE;
-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.EXTRA_HALF_SHEET_TYPE;
-
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
 import android.app.KeyguardManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothManager;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
+import android.nearby.FastPairDevice;
 import android.nearby.NearbyDevice;
+import android.nearby.NearbyManager;
 import android.nearby.ScanCallback;
-import android.os.Bundle;
-import android.os.UserHandle;
+import android.nearby.ScanRequest;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -57,15 +49,15 @@
 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.HalfSheetCallback;
+import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager;
 import com.android.server.nearby.fastpair.pairinghandler.PairingProgressHandlerBase;
-import com.android.server.nearby.provider.FastPairDataProvider;
-import com.android.server.nearby.util.Environment;
+import com.android.server.nearby.util.FastPairDecoder;
+import com.android.server.nearby.util.ForegroundThread;
+import com.android.server.nearby.util.Hex;
 
 import com.google.protobuf.ByteString;
 
 import java.security.GeneralSecurityException;
-import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
@@ -73,7 +65,6 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-import java.util.stream.Collectors;
 
 import service.proto.Cache;
 import service.proto.Rpcs;
@@ -86,7 +77,6 @@
 public class FastPairManager {
     private static final String ACTION_PREFIX = UserActionHandler.PREFIX;
     private static final int WAIT_FOR_UNLOCK_MILLIS = 5000;
-    private static final String ACTIVITY_INTENT_ACTION = "android.nearby.SHOW_HALFSHEET";
     /** A notification ID which should be dismissed */
     public static final String EXTRA_NOTIFICATION_ID = ACTION_PREFIX + "EXTRA_NOTIFICATION_ID";
     public static final String ACTION_RESOURCES_APK = "android.nearby.SHOW_HALFSHEET";
@@ -97,8 +87,6 @@
 
     private static Executor sFastPairExecutor;
 
-    private String mHalfSheetApkPkgName;
-
     final LocatorContextWrapper mLocatorContextWrapper;
     final IntentFilter mIntentFilter;
     final Locator mLocator;
@@ -107,16 +95,29 @@
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
                 Log.d("FastPairService", " screen on");
+                NearbyManager nearbyManager = (NearbyManager) mLocatorContextWrapper
+                        .getApplicationContext().getSystemService(Context.NEARBY_SERVICE);
+
+                Log.d("FastPairService", " the nearby manager is " + nearbyManager);
+
+                if (nearbyManager != null) {
+                    nearbyManager.startScan(
+                            new ScanRequest.Builder()
+                                    .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR).build(),
+                            ForegroundThread.getExecutor(),
+                            mScanCallback);
+                } else {
+                    Log.d("FastPairService", " the nearby manager is null");
+                }
+
             } else if (intent.getAction().equals(ACTION_START_PAIRING)) {
                 byte[] model = intent.getByteArrayExtra(EXTRA_MODEL_ID);
                 String address = intent.getStringExtra(EXTRA_ADDRESS);
                 Log.d("FastPairService", "start pair " + address);
-                Rpcs.GetObservedDeviceResponse response =
-                        FastPairDataProvider.getInstance().loadFastPairDeviceMetadata(model);
-                ByteString publicKey = response.getDevice().getAntiSpoofingKeyPair().getPublicKey();
-                showHalfSheet(Cache.ScanFastPairStoreItem.newBuilder().setAddress(address)
-                        .setAntiSpoofingPublicKey(publicKey)
-                        .build());
+                Locator.get(mLocatorContextWrapper, FastPairHalfSheetManager.class).showHalfSheet(
+                        Cache.ScanFastPairStoreItem.newBuilder().setAddress(address)
+                                .setAntiSpoofingPublicKey(ByteString.EMPTY)
+                                .build());
             } else {
                 Log.d("FastPairService", " screen off");
             }
@@ -136,17 +137,23 @@
     final ScanCallback mScanCallback = new ScanCallback() {
         @Override
         public void onDiscovered(@NonNull NearbyDevice device) {
-            Log.d("FastPair", "Ondiscovery " + device.getName());
+            Locator.get(mLocatorContextWrapper, FastPairAdvHandler.class).handleBroadcast(device);
         }
 
         @Override
         public void onUpdated(@NonNull NearbyDevice device) {
-
+            FastPairDevice fastPairDevice = (FastPairDevice) device;
+            byte[] modelArray = FastPairDecoder.getModelId(fastPairDevice.getData());
+            Log.d("FastPairService",
+                    "update model id" + Hex.bytesToStringLowercase(modelArray));
         }
 
         @Override
         public void onLost(@NonNull NearbyDevice device) {
-
+            FastPairDevice fastPairDevice = (FastPairDevice) device;
+            byte[] modelArray = FastPairDecoder.getModelId(fastPairDevice.getData());
+            Log.d("FastPairService",
+                    "lost model id" + Hex.bytesToStringLowercase(modelArray));
         }
     };
 
@@ -156,7 +163,7 @@
     public void initiate() {
         mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
         mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
-        mIntentFilter.addAction("NEARBY_START_PAIRING");
+        mIntentFilter.addAction(ACTION_START_PAIRING);
 
         mLocatorContextWrapper.getContext()
                 .registerReceiver(mScreenBroadcastReceiver, mIntentFilter);
@@ -291,7 +298,7 @@
         }
     }
 
-    /** Check if the pairing is initial pairing with fast pair 2.0 design. */
+    /** Checks if the pairing is initial pairing with fast pair 2.0 design. */
     public static boolean isThroughFastPair2InitialPairing(
             DiscoveryItem item, @Nullable byte[] accountKey) {
         return accountKey == null && item.getAuthenticationPublicKeySecp256R1() != null;
@@ -338,69 +345,4 @@
         return manager == null ? null : manager.getAdapter();
     }
 
-    /**
-     * Gets the package name of HalfSheet.apk
-     * getHalfSheetApkPkgName may invoke PackageManager multiple times and it does not have
-     * race condition check. Since there is no lock for mHalfSheetApkPkgName.
-     */
-    String getHalfSheetApkPkgName() {
-        if (mHalfSheetApkPkgName != null) {
-            return mHalfSheetApkPkgName;
-        }
-        List<ResolveInfo> resolveInfos = mLocatorContextWrapper.getContext()
-                .getPackageManager().queryIntentActivities(
-                        new Intent(ACTION_RESOURCES_APK),
-                        PackageManager.MATCH_SYSTEM_ONLY);
-
-        // remove apps that don't live in the nearby apex
-        resolveInfos.removeIf(info ->
-                !Environment.isAppInNearbyApex(info.activityInfo.applicationInfo));
-
-        if (resolveInfos.isEmpty()) {
-            // Resource APK not loaded yet, print a stack trace to see where this is called from
-            Log.e("FastPairManager", "Attempted to fetch resources before halfsheet "
-                            + " APK is installed or package manager can't resolve correctly!",
-                    new IllegalStateException());
-            return null;
-        }
-
-        if (resolveInfos.size() > 1) {
-            // multiple apps found, log a warning, but continue
-            Log.w("FastPairManager", "Found > 1 APK that can resolve halfsheet APK intent: "
-                    + resolveInfos.stream()
-                    .map(info -> info.activityInfo.applicationInfo.packageName)
-                    .collect(Collectors.joining(", ")));
-        }
-
-        // Assume the first ResolveInfo is the one we're looking for
-        ResolveInfo info = resolveInfos.get(0);
-        mHalfSheetApkPkgName = info.activityInfo.applicationInfo.packageName;
-        Log.i("FastPairManager", "Found halfsheet APK at: " + mHalfSheetApkPkgName);
-        return mHalfSheetApkPkgName;
-    }
-
-    /**
-     * Invokes half sheet in the other apk. This function can only be called in Nearby because other
-     * app can't get the correct component name.
-     */
-    void showHalfSheet(Cache.ScanFastPairStoreItem scanFastPairStoreItem) {
-        if (mLocatorContextWrapper != null) {
-            String packageName = getHalfSheetApkPkgName();
-            HalfSheetCallback callback = new HalfSheetCallback();
-            callback.setmFastPairController(Locator
-                    .getFromContextWrapper(mLocatorContextWrapper, FastPairController.class));
-            Bundle bundle = new Bundle();
-            bundle.putBinder(EXTRA_BINDER, callback);
-            mLocatorContextWrapper.getContext()
-                    .startActivityAsUser(new Intent(ACTIVITY_INTENT_ACTION)
-                                    .putExtra(EXTRA_HALF_SHEET_INFO,
-                                            scanFastPairStoreItem.toByteArray())
-                                    .putExtra(EXTRA_HALF_SHEET_TYPE, DEVICE_PAIRING_FRAGMENT_TYPE)
-                                    .putExtra(EXTRA_BUNDLE, bundle)
-                                    .setComponent(new ComponentName(packageName,
-                                            packageName + ".HalfSheetActivity")),
-                            UserHandle.CURRENT);
-
-        }
-    }
 }
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairModule.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairModule.java
index e92bb8a..d7946d1 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairModule.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairModule.java
@@ -49,7 +49,9 @@
         } else if (type.equals(FastPairCacheManager.class)) {
             locator.bind(FastPairCacheManager.class, new FastPairCacheManager(context));
         } else if (type.equals(FastPairHalfSheetManager.class)) {
-            locator.bind(FastPairHalfSheetManager.class, new FastPairHalfSheetManager());
+            locator.bind(FastPairHalfSheetManager.class, new FastPairHalfSheetManager(context));
+        } else if (type.equals(FastPairAdvHandler.class)) {
+            locator.bind(FastPairAdvHandler.class, new FastPairAdvHandler(context));
         } else if (type.equals(Clock.class)) {
             locator.bind(Clock.class, new Clock() {
                 @Override
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 4c43965..9fdd01a 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
@@ -16,15 +16,73 @@
 
 package com.android.server.nearby.fastpair.halfsheet;
 
+import static com.android.server.nearby.fastpair.Constant.DEVICE_PAIRING_FRAGMENT_TYPE;
+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.EXTRA_HALF_SHEET_TYPE;
+import static com.android.server.nearby.fastpair.FastPairManager.ACTION_RESOURCES_APK;
+
 import android.bluetooth.BluetoothDevice;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
 import android.util.Log;
 
+import com.android.server.nearby.common.locator.Locator;
+import com.android.server.nearby.fastpair.FastPairController;
 import com.android.server.nearby.fastpair.cache.DiscoveryItem;
+import com.android.server.nearby.util.Environment;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import service.proto.Cache;
 
 /**
  * Fast Pair ux manager for half sheet.
  */
 public class FastPairHalfSheetManager {
+    private static final String ACTIVITY_INTENT_ACTION = "android.nearby.SHOW_HALFSHEET";
+
+    private String mHalfSheetApkPkgName;
+    private Context mContext;
+
+    /**
+     * Construct function
+     */
+    public FastPairHalfSheetManager(Context context) {
+        mContext = context;
+    }
+
+
+    /**
+     * Invokes half sheet in the other apk. This function can only be called in Nearby because other
+     * app can't get the correct component name.
+     */
+    public void showHalfSheet(Cache.ScanFastPairStoreItem scanFastPairStoreItem) {
+        if (mContext != null) {
+            String packageName = getHalfSheetApkPkgName();
+            HalfSheetCallback callback = new HalfSheetCallback();
+            callback.setmFastPairController(Locator.get(mContext, FastPairController.class));
+            Bundle bundle = new Bundle();
+            bundle.putBinder(EXTRA_BINDER, callback);
+            mContext
+                    .startActivityAsUser(new Intent(ACTIVITY_INTENT_ACTION)
+                                    .putExtra(EXTRA_HALF_SHEET_INFO,
+                                            scanFastPairStoreItem.toByteArray())
+                                    .putExtra(EXTRA_HALF_SHEET_TYPE, DEVICE_PAIRING_FRAGMENT_TYPE)
+                                    .putExtra(EXTRA_BUNDLE, bundle)
+                                    .setComponent(new ComponentName(packageName,
+                                            packageName + ".HalfSheetActivity")),
+                            UserHandle.CURRENT);
+
+        }
+    }
 
     /**
      * Shows pairing fail half sheet.
@@ -77,4 +135,45 @@
      */
     public void notifyPairingProcessDone(boolean success, String address, DiscoveryItem item) {
     }
+
+    /**
+     * Gets the package name of HalfSheet.apk
+     * getHalfSheetApkPkgName may invoke PackageManager multiple times and it does not have
+     * race condition check. Since there is no lock for mHalfSheetApkPkgName.
+     */
+    String getHalfSheetApkPkgName() {
+        if (mHalfSheetApkPkgName != null) {
+            return mHalfSheetApkPkgName;
+        }
+        List<ResolveInfo> resolveInfos = mContext
+                .getPackageManager().queryIntentActivities(
+                        new Intent(ACTION_RESOURCES_APK),
+                        PackageManager.MATCH_SYSTEM_ONLY);
+
+        // remove apps that don't live in the nearby apex
+        resolveInfos.removeIf(info ->
+                !Environment.isAppInNearbyApex(info.activityInfo.applicationInfo));
+
+        if (resolveInfos.isEmpty()) {
+            // Resource APK not loaded yet, print a stack trace to see where this is called from
+            Log.e("FastPairManager", "Attempted to fetch resources before halfsheet "
+                            + " APK is installed or package manager can't resolve correctly!",
+                    new IllegalStateException());
+            return null;
+        }
+
+        if (resolveInfos.size() > 1) {
+            // multiple apps found, log a warning, but continue
+            Log.w("FastPairManager", "Found > 1 APK that can resolve halfsheet APK intent: "
+                    + resolveInfos.stream()
+                    .map(info -> info.activityInfo.applicationInfo.packageName)
+                    .collect(Collectors.joining(", ")));
+        }
+
+        // Assume the first ResolveInfo is the one we're looking for
+        ResolveInfo info = resolveInfos.get(0);
+        mHalfSheetApkPkgName = info.activityInfo.applicationInfo.packageName;
+        Log.i("FastPairManager", "Found halfsheet APK at: " + mHalfSheetApkPkgName);
+        return mHalfSheetApkPkgName;
+    }
 }
diff --git a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
index fb1afb0..72c4b75 100644
--- a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
+++ b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
@@ -22,6 +22,7 @@
 import android.nearby.IScanListener;
 import android.nearby.NearbyDeviceParcelable;
 import android.nearby.ScanRequest;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -43,13 +44,13 @@
     private @ScanRequest.ScanMode int mScanMode;
 
     @GuardedBy("mLock")
-    private Map<IScanListener, ScanListenerRecord> mScanTypeScanListenerRecordMap;
+    private Map<IBinder, ScanListenerRecord> mScanTypeScanListenerRecordMap;
 
     @Override
     public void onNearbyDeviceDiscovered(NearbyDeviceParcelable nearbyDevice) {
         synchronized (mLock) {
-            for (IScanListener listener : mScanTypeScanListenerRecordMap.keySet()) {
-                ScanListenerRecord record = mScanTypeScanListenerRecordMap.get(listener);
+            for (IBinder listenerBinder : mScanTypeScanListenerRecordMap.keySet()) {
+                ScanListenerRecord record = mScanTypeScanListenerRecordMap.get(listenerBinder);
                 if (record == null) {
                     Log.w(TAG, "DiscoveryProviderManager cannot find the scan record.");
                     continue;
@@ -76,9 +77,10 @@
      */
     public void registerScanListener(ScanRequest scanRequest, IScanListener listener) {
         synchronized (mLock) {
-            if (mScanTypeScanListenerRecordMap.containsKey(listener)) {
-                ScanRequest savedScanRequest = mScanTypeScanListenerRecordMap.get(
-                        listener).getScanRequest();
+            IBinder listenerBinder = listener.asBinder();
+            if (mScanTypeScanListenerRecordMap.containsKey(listener.asBinder())) {
+                ScanRequest savedScanRequest = mScanTypeScanListenerRecordMap
+                        .get(listenerBinder).getScanRequest();
                 if (scanRequest.equals(savedScanRequest)) {
                     Log.d(TAG, "Already registered the scanRequest: " + scanRequest);
                     return;
@@ -87,7 +89,7 @@
 
             startProviders(scanRequest);
 
-            mScanTypeScanListenerRecordMap.put(listener,
+            mScanTypeScanListenerRecordMap.put(listenerBinder,
                     new ScanListenerRecord(scanRequest, listener));
             if (mScanMode < scanRequest.getScanMode()) {
                 mScanMode = scanRequest.getScanMode();
@@ -100,15 +102,17 @@
      * Unregisters the listener in the manager and adjusts the scan mode if necessary afterwards.
      */
     public void unregisterScanListener(IScanListener listener) {
+        IBinder listenerBinder = listener.asBinder();
         synchronized (mLock) {
-            if (!mScanTypeScanListenerRecordMap.containsKey(listener)) {
+            if (!mScanTypeScanListenerRecordMap.containsKey(listenerBinder)) {
                 Log.w(TAG,
-                        "Cannot unregister the scanRequest %s because the request is never "
+                        "Cannot unregister the scanRequest because the request is never "
                                 + "registered.");
                 return;
             }
 
-            ScanListenerRecord removedRecord = mScanTypeScanListenerRecordMap.remove(listener);
+            ScanListenerRecord removedRecord = mScanTypeScanListenerRecordMap
+                    .remove(listenerBinder);
             if (mScanTypeScanListenerRecordMap.isEmpty()) {
                 stopProviders();
                 return;
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 b7c63c9..cac815b 100644
--- a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
@@ -32,17 +32,18 @@
 
     private static FastPairDataProvider sInstance;
 
-    private ProxyFastPairDataProvider mProxyProvider;
+    private ProxyFastPairDataProvider mProxyFastPairDataProvider;
 
     /** Initializes FastPairDataProvider singleton. */
     public static synchronized FastPairDataProvider init(Context context) {
+
         if (sInstance == null) {
             sInstance = new FastPairDataProvider(context);
         }
-        if (sInstance.mProxyProvider == null) {
+        if (sInstance.mProxyFastPairDataProvider == null) {
             Log.wtf(TAG, "no proxy fast pair data provider found");
         } else {
-            sInstance.mProxyProvider.register();
+            sInstance.mProxyFastPairDataProvider.register();
         }
         return sInstance;
     }
@@ -53,16 +54,18 @@
     }
 
     private FastPairDataProvider(Context context) {
-        mProxyProvider = ProxyFastPairDataProvider.create(
+        mProxyFastPairDataProvider = ProxyFastPairDataProvider.create(
                 context, FastPairDataProviderBase.ACTION_FAST_PAIR_DATA_PROVIDER);
+        Log.d("FastPairService", "the fast pair proxy provider is init"
+                + (mProxyFastPairDataProvider == null));
     }
 
     /** loadFastPairDeviceMetadata. */
     @WorkerThread
     @Nullable
     public Rpcs.GetObservedDeviceResponse loadFastPairDeviceMetadata(byte[] modelId) {
-        if (mProxyProvider != null) {
-            return mProxyProvider.loadFastPairDeviceMetadata(modelId);
+        if (mProxyFastPairDataProvider != null) {
+            return mProxyFastPairDataProvider.loadFastPairDeviceMetadata(modelId);
         }
         throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed");
     }
diff --git a/nearby/service/java/com/android/server/nearby/util/FastPairDecoder.java b/nearby/service/java/com/android/server/nearby/util/FastPairDecoder.java
new file mode 100644
index 0000000..5bb76c9
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/util/FastPairDecoder.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.util;
+
+import android.annotation.Nullable;
+import android.bluetooth.le.ScanRecord;
+import android.os.ParcelUuid;
+import android.util.SparseArray;
+
+import com.android.server.nearby.common.ble.BleFilter;
+import com.android.server.nearby.common.ble.BleRecord;
+
+import java.util.Arrays;
+
+/**
+ * Parses Fast Pair information out of {@link BleRecord}s.
+ *
+ * <p>There are 2 different packet formats that are supported, which is used can be determined by
+ * packet length:
+ *
+ * <p>For 3-byte packets, the full packet is the model ID.
+ *
+ * <p>For all other packets, the first byte is the header, followed by the model ID, followed by
+ * zero or more extra fields. Each field has its own header byte followed by the field value. The
+ * packet header is formatted as 0bVVVLLLLR (V = version, L = model ID length, R = reserved) and
+ * each extra field header is 0bLLLLTTTT (L = field length, T = field type).
+ */
+public class FastPairDecoder {
+
+    private static final int FIELD_TYPE_BLOOM_FILTER = 0;
+    private static final int FIELD_TYPE_BLOOM_FILTER_SALT = 1;
+    private static final int FIELD_TYPE_BLOOM_FILTER_NO_NOTIFICATION = 2;
+    private static final int FIELD_TYPE_BATTERY = 3;
+    private static final int FIELD_TYPE_BATTERY_NO_NOTIFICATION = 4;
+    public static final int FIELD_TYPE_CONNECTION_STATE = 5;
+    private static final int FIELD_TYPE_RANDOM_RESOLVABLE_DATA = 6;
+
+
+    /** FE2C is the 16-bit Service UUID. The rest is the base UUID. See BluetoothUuid (hidden). */
+    private static final ParcelUuid FAST_PAIR_SERVICE_PARCEL_UUID =
+            ParcelUuid.fromString("0000FE2C-0000-1000-8000-00805F9B34FB");
+
+    /** The filter you use to scan for Fast Pair BLE advertisements. */
+    public static final BleFilter FILTER =
+            new BleFilter.Builder().setServiceData(FAST_PAIR_SERVICE_PARCEL_UUID,
+                    new byte[0]).build();
+
+    // NOTE: Ensure that all bitmasks are always ints, not bytes so that bitshifting works correctly
+    // without needing worry about signing errors.
+    private static final int HEADER_VERSION_BITMASK = 0b11100000;
+    private static final int HEADER_LENGTH_BITMASK = 0b00011110;
+    private static final int HEADER_VERSION_OFFSET = 5;
+    private static final int HEADER_LENGTH_OFFSET = 1;
+
+    private static final int EXTRA_FIELD_LENGTH_BITMASK = 0b11110000;
+    private static final int EXTRA_FIELD_TYPE_BITMASK = 0b00001111;
+    private static final int EXTRA_FIELD_LENGTH_OFFSET = 4;
+    private static final int EXTRA_FIELD_TYPE_OFFSET = 0;
+
+    private static final int MIN_ID_LENGTH = 3;
+    private static final int MAX_ID_LENGTH = 14;
+    private static final int HEADER_INDEX = 0;
+    private static final int HEADER_LENGTH = 1;
+    private static final int FIELD_HEADER_LENGTH = 1;
+
+    // Not using java.util.IllegalFormatException because it is unchecked.
+    private static class IllegalFormatException extends Exception {
+        private IllegalFormatException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * Gets model id data from broadcast
+     */
+    @Nullable
+    public static byte[] getModelId(@Nullable byte[] serviceData) {
+        if (serviceData == null) {
+            return null;
+        }
+
+        if (serviceData.length >= MIN_ID_LENGTH) {
+            if (serviceData.length == MIN_ID_LENGTH) {
+                // If the length == 3, all bytes are the ID. See flag docs for more about
+                // endianness.
+                return serviceData;
+            } else {
+                // Otherwise, the first byte is a header which contains the length of the big-endian
+                // model ID that follows. The model ID will be trimmed if it contains leading zeros.
+                int idIndex = 1;
+                int end = idIndex + getIdLength(serviceData);
+                while (serviceData[idIndex] == 0 && end - idIndex > MIN_ID_LENGTH) {
+                    idIndex++;
+                }
+                return Arrays.copyOfRange(serviceData, idIndex, end);
+            }
+        }
+        return null;
+    }
+
+    /** Gets the FastPair service data array if available, otherwise returns null. */
+    @Nullable
+    public static byte[] getServiceDataArray(BleRecord bleRecord) {
+        return bleRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID);
+    }
+
+    /** Gets the FastPair service data array if available, otherwise returns null. */
+    @Nullable
+    public static byte[] getServiceDataArray(ScanRecord scanRecord) {
+        return scanRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID);
+    }
+
+    /** Gets the bloom filter from the extra fields if available, otherwise returns null. */
+    @Nullable
+    public static byte[] getBloomFilter(@Nullable byte[] serviceData) {
+        return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER);
+    }
+
+    /** Gets the bloom filter salt from the extra fields if available, otherwise returns null. */
+    @Nullable
+    public static byte[] getBloomFilterSalt(byte[] serviceData) {
+        return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER_SALT);
+    }
+
+    /**
+     * Gets the suppress notification with bloom filter from the extra fields if available,
+     * otherwise returns null.
+     */
+    @Nullable
+    public static byte[] getBloomFilterNoNotification(@Nullable byte[] serviceData) {
+        return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER_NO_NOTIFICATION);
+    }
+
+
+    /**
+     * Get random resolvableData
+     */
+    @Nullable
+    public static byte[] getRandomResolvableData(byte[] serviceData) {
+        return getExtraField(serviceData, FIELD_TYPE_RANDOM_RESOLVABLE_DATA);
+    }
+
+    @Nullable
+    private static byte[] getExtraField(@Nullable byte[] serviceData, int fieldId) {
+        if (serviceData == null || serviceData.length < HEADER_INDEX + HEADER_LENGTH) {
+            return null;
+        }
+        try {
+            return getExtraFields(serviceData).get(fieldId);
+        } catch (IllegalFormatException e) {
+            return null;
+        }
+    }
+
+    /** Gets extra field data at the end of the packet, defined by the extra field header. */
+    private static SparseArray<byte[]> getExtraFields(byte[] serviceData)
+            throws IllegalFormatException {
+        SparseArray<byte[]> extraFields = new SparseArray<>();
+        if (getVersion(serviceData) != 0) {
+            return extraFields;
+        }
+        int headerIndex = getFirstExtraFieldHeaderIndex(serviceData);
+        while (headerIndex < serviceData.length) {
+            int length = getExtraFieldLength(serviceData, headerIndex);
+            int index = headerIndex + FIELD_HEADER_LENGTH;
+            int type = getExtraFieldType(serviceData, headerIndex);
+            int end = index + length;
+            if (extraFields.get(type) == null) {
+                if (end <= serviceData.length) {
+                    extraFields.put(type, Arrays.copyOfRange(serviceData, index, end));
+                } else {
+                    throw new IllegalFormatException(
+                            "Invalid length, " + end + " is longer than service data size "
+                                    + serviceData.length);
+                }
+            }
+            headerIndex = end;
+        }
+        return extraFields;
+    }
+
+    /** Checks whether or not a valid ID is included in the service data packet. */
+    public static boolean hasBeaconIdBytes(BleRecord bleRecord) {
+        byte[] serviceData = bleRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID);
+        return checkModelId(serviceData);
+    }
+
+    /** Check whether byte array is FastPair model id or not. */
+    public static boolean checkModelId(@Nullable byte[] scanResult) {
+        return scanResult != null
+                // The 3-byte format has no header byte (all bytes are the ID).
+                && (scanResult.length == MIN_ID_LENGTH
+                // Header byte exists. We support only format version 0. (A different version
+                // indicates
+                // a breaking change in the format.)
+                || (scanResult.length > MIN_ID_LENGTH
+                && getVersion(scanResult) == 0
+                && isIdLengthValid(scanResult)));
+    }
+
+    /** Checks whether or not bloom filter is included in the service data packet. */
+    public static boolean hasBloomFilter(BleRecord bleRecord) {
+        return (getBloomFilter(getServiceDataArray(bleRecord)) != null
+                || getBloomFilterNoNotification(getServiceDataArray(bleRecord)) != null);
+    }
+
+    /** Checks whether or not bloom filter is included in the service data packet. */
+    public static boolean hasBloomFilter(ScanRecord scanRecord) {
+        return (getBloomFilter(getServiceDataArray(scanRecord)) != null
+                || getBloomFilterNoNotification(getServiceDataArray(scanRecord)) != null);
+    }
+
+    private static int getVersion(byte[] serviceData) {
+        return serviceData.length == MIN_ID_LENGTH
+                ? 0
+                : (serviceData[HEADER_INDEX] & HEADER_VERSION_BITMASK) >> HEADER_VERSION_OFFSET;
+    }
+
+    private static int getIdLength(byte[] serviceData) {
+        return serviceData.length == MIN_ID_LENGTH
+                ? MIN_ID_LENGTH
+                : (serviceData[HEADER_INDEX] & HEADER_LENGTH_BITMASK) >> HEADER_LENGTH_OFFSET;
+    }
+
+    private static int getFirstExtraFieldHeaderIndex(byte[] serviceData) {
+        return HEADER_INDEX + HEADER_LENGTH + getIdLength(serviceData);
+    }
+
+    private static int getExtraFieldLength(byte[] serviceData, int extraFieldIndex) {
+        return (serviceData[extraFieldIndex] & EXTRA_FIELD_LENGTH_BITMASK)
+                >> EXTRA_FIELD_LENGTH_OFFSET;
+    }
+
+    private static int getExtraFieldType(byte[] serviceData, int extraFieldIndex) {
+        return (serviceData[extraFieldIndex] & EXTRA_FIELD_TYPE_BITMASK) >> EXTRA_FIELD_TYPE_OFFSET;
+    }
+
+    private static boolean isIdLengthValid(byte[] serviceData) {
+        int idLength = getIdLength(serviceData);
+        return MIN_ID_LENGTH <= idLength
+                && idLength <= MAX_ID_LENGTH
+                && idLength + HEADER_LENGTH <= serviceData.length;
+    }
+}
+
diff --git a/nearby/service/java/com/android/server/nearby/util/Hex.java b/nearby/service/java/com/android/server/nearby/util/Hex.java
new file mode 100644
index 0000000..c827ec5
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/util/Hex.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.util;
+
+/**
+ * Hex class that contains hex related functions.
+ */
+public class Hex {
+    private static final char[] HEX_LOWERCASE = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    /**
+     * Bytes array to lower case string.
+     */
+    public static String bytesToStringLowercase(byte[] bytes) {
+        char[] hexChars = new char[bytes.length * 2];
+        int j = 0;
+        for (int i = 0; i < bytes.length; i++) {
+            int v = bytes[i] & 0xFF;
+            hexChars[j++] = HEX_LOWERCASE[v >>> 4];
+            hexChars[j++] = HEX_LOWERCASE[v & 0x0F];
+        }
+        return new String(hexChars);
+    }
+}
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 bb7e7c2..8611611 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/FastPairDataProviderBaseTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/FastPairDataProviderBaseTest.java
@@ -25,7 +25,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.MockitoAnnotations.initMocks;
 
-import android.nearby.FastPairAccountKeyDeviceMetadata;
+import android.accounts.Account;
 import android.nearby.FastPairAntispoofkeyDeviceMetadata;
 import android.nearby.FastPairDataProviderBase;
 import android.nearby.FastPairDeviceMetadata;
@@ -49,14 +49,15 @@
 import androidx.annotation.NonNull;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 
-import java.util.ArrayList;
-
 @RunWith(AndroidJUnit4.class)
 public class FastPairDataProviderBaseTest {
 
@@ -75,6 +76,17 @@
     private static final int DEVICE_TYPE = 7;
     private static final String DOWNLOAD_COMPANION_APP_DESCRIPTION =
             "DOWNLOAD_COMPANION_APP_DESCRIPTION";
+    private static final int ELIGIBLE_ACCOUNTS_NUM = 2;
+    private static final Account ELIGIBLE_ACCOUNT_1 = new Account("abc@google.com", "type1");
+    private static final boolean ELIGIBLE_ACCOUNT_1_OPT_IN = true;
+    private static final Account ELIGIBLE_ACCOUNT_2 = new Account("def@gmail.com", "type2");
+    private static final boolean ELIGIBLE_ACCOUNT_2_OPT_IN = false;
+    private static final ImmutableList<FastPairEligibleAccount> ELIGIBLE_ACCOUNTS =
+            ImmutableList.of(
+                    genHappyPathFastPairEligibleAccount(ELIGIBLE_ACCOUNT_1,
+                            ELIGIBLE_ACCOUNT_1_OPT_IN),
+                    genHappyPathFastPairEligibleAccount(ELIGIBLE_ACCOUNT_2,
+                            ELIGIBLE_ACCOUNT_2_OPT_IN));
     private static final int ERROR_CODE_BAD_REQUEST =
             FastPairDataProviderBase.ERROR_CODE_BAD_REQUEST;
     private static final String ERROR_STRING = "ERROR_STRING";
@@ -116,10 +128,16 @@
     private static final FastPairAntispoofkeyDeviceMetadataRequestParcel
             FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA_REQUEST_PARCEL =
             genFastPairAntispoofkeyDeviceMetadataRequestParcel();
+    private static final FastPairEligibleAccountsRequestParcel
+            FAST_PAIR_ELIGIBLE_ACCOUNTS_REQUEST_PARCEL =
+            genFastPairEligibleAccountsRequestParcel();
     private static final FastPairAntispoofkeyDeviceMetadata
             HAPPY_PATH_FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA =
             genHappyPathFastPairAntispoofkeyDeviceMetadata();
 
+    private @Captor ArgumentCaptor<FastPairEligibleAccountParcel[]>
+            mFastPairEligibleAccountParcelsArgumentCaptor;
+
     private @Mock FastPairDataProviderBase mMockFastPairDataProviderBase;
     private @Mock IFastPairAntispoofkeyDeviceMetadataCallback.Stub
             mAntispoofkeyDeviceMetadataCallback;
@@ -181,14 +199,25 @@
 
     @Test
     public void testHappyPathLoadFastPairEligibleAccounts() throws Exception {
+        // AOSP sends calls to OEM via Parcelable.
         mHappyPathFastPairDataProvider.asProvider().loadFastPairEligibleAccounts(
-                new FastPairEligibleAccountsRequestParcel(),
+                FAST_PAIR_ELIGIBLE_ACCOUNTS_REQUEST_PARCEL,
                 mEligibleAccountsCallback);
+
+        // OEM receives request and verifies that it is as expected.
+        final ArgumentCaptor<FastPairDataProviderBase.FastPairEligibleAccountsRequest>
+                mFastPairEligibleAccountsRequestCaptor =
+                ArgumentCaptor.forClass(
+                        FastPairDataProviderBase.FastPairEligibleAccountsRequest.class);
         verify(mMockFastPairDataProviderBase).onLoadFastPairEligibleAccounts(
-                any(FastPairDataProviderBase.FastPairEligibleAccountsRequest.class),
+                mFastPairEligibleAccountsRequestCaptor.capture(),
                 any(FastPairDataProviderBase.FastPairEligibleAccountsCallback.class));
+        ensureHappyPathAsExpected(mFastPairEligibleAccountsRequestCaptor.getValue());
+
+        // AOSP receives responses and verifies that it is as expected.
         verify(mEligibleAccountsCallback).onFastPairEligibleAccountsReceived(
-                any(FastPairEligibleAccountParcel[].class));
+                mFastPairEligibleAccountParcelsArgumentCaptor.capture());
+        ensureHappyPathAsExpected(mFastPairEligibleAccountParcelsArgumentCaptor.getValue());
     }
 
     @Test
@@ -239,12 +268,13 @@
     @Test
     public void testErrorPathLoadFastPairEligibleAccounts() throws Exception {
         mErrorPathFastPairDataProvider.asProvider().loadFastPairEligibleAccounts(
-                new FastPairEligibleAccountsRequestParcel(),
+                FAST_PAIR_ELIGIBLE_ACCOUNTS_REQUEST_PARCEL,
                 mEligibleAccountsCallback);
         verify(mMockFastPairDataProviderBase).onLoadFastPairEligibleAccounts(
                 any(FastPairDataProviderBase.FastPairEligibleAccountsRequest.class),
                 any(FastPairDataProviderBase.FastPairEligibleAccountsCallback.class));
-        verify(mEligibleAccountsCallback).onError(anyInt(), any());
+        verify(mEligibleAccountsCallback).onError(
+                eq(ERROR_CODE_BAD_REQUEST), eq(ERROR_STRING));
     }
 
     @Test
@@ -298,8 +328,7 @@
                 @NonNull FastPairAccountDevicesMetadataCallback callback) {
             mMockFastPairDataProviderBase.onLoadFastPairAccountDevicesMetadata(
                     request, callback);
-            callback.onFastPairAccountDevicesMetadataReceived(
-                    new ArrayList<FastPairAccountKeyDeviceMetadata>());
+            callback.onFastPairAccountDevicesMetadataReceived(ImmutableList.of());
         }
 
         @Override
@@ -308,8 +337,7 @@
                 @NonNull FastPairEligibleAccountsCallback callback) {
             mMockFastPairDataProviderBase.onLoadFastPairEligibleAccounts(
                     request, callback);
-            callback.onFastPairEligibleAccountsReceived(
-                    new ArrayList<FastPairEligibleAccount>());
+            callback.onFastPairEligibleAccountsReceived(ELIGIBLE_ACCOUNTS);
         }
 
         @Override
@@ -394,6 +422,15 @@
         return requestParcel;
     }
 
+    /* Generates FastPairEligibleAccountsRequestParcel. */
+    private static FastPairEligibleAccountsRequestParcel
+            genFastPairEligibleAccountsRequestParcel() {
+        FastPairEligibleAccountsRequestParcel requestParcel =
+                new FastPairEligibleAccountsRequestParcel();
+        // No fields since FastPairEligibleAccountsRequestParcel is just a place holder now.
+        return requestParcel;
+    }
+
     /* Generates Happy Path AntispoofkeyDeviceMetadata. */
     private static FastPairAntispoofkeyDeviceMetadata
             genHappyPathFastPairAntispoofkeyDeviceMetadata() {
@@ -448,12 +485,28 @@
         return builder.build();
     }
 
+    /* Generates Happy Path FastPairEligibleAccount. */
+    private static FastPairEligibleAccount genHappyPathFastPairEligibleAccount(
+            Account account, boolean optIn) {
+        FastPairEligibleAccount.Builder builder = new FastPairEligibleAccount.Builder();
+        builder.setAccount(account);
+        builder.setOptIn(optIn);
+
+        return builder.build();
+    }
+
     /* Verifies Happy Path AntispoofkeyDeviceMetadataRequest. */
     private static void ensureHappyPathAsExpected(
             FastPairDataProviderBase.FastPairAntispoofkeyDeviceMetadataRequest request) {
         assertEquals(REQUEST_MODEL_ID, request.getModelId());
     }
 
+    @SuppressWarnings("UnusedVariable")
+    private static void ensureHappyPathAsExpected(
+            FastPairDataProviderBase.FastPairEligibleAccountsRequest request) {
+        // No fields since FastPairEligibleAccountsRequest is just a place holder now.
+    }
+
     /* Verifies Happy Path AntispoofkeyDeviceMetadataParcel. */
     private static void ensureHappyPathAsExpected(
             FastPairAntispoofkeyDeviceMetadataParcel metadataParcel) {
@@ -510,4 +563,14 @@
         assertEquals(WAIT_LAUNCH_COMPANION_APP_DESCRIPTION,
                 metadataParcel.waitLaunchCompanionAppDescription);
     }
+
+    /* Verifies Happy Path EligibleAccounts. */
+    private static void ensureHappyPathAsExpected(FastPairEligibleAccountParcel[] accountsParcel) {
+        assertEquals(ELIGIBLE_ACCOUNTS_NUM, accountsParcel.length);
+        assertEquals(ELIGIBLE_ACCOUNT_1, accountsParcel[0].account);
+        assertEquals(ELIGIBLE_ACCOUNT_1_OPT_IN, accountsParcel[0].optIn);
+
+        assertEquals(ELIGIBLE_ACCOUNT_2, accountsParcel[1].account);
+        assertEquals(ELIGIBLE_ACCOUNT_2_OPT_IN, accountsParcel[1].optIn);
+    }
 }
diff --git a/nearby/tests/unit/Android.bp b/nearby/tests/unit/Android.bp
index 5287891..64ef292 100644
--- a/nearby/tests/unit/Android.bp
+++ b/nearby/tests/unit/Android.bp
@@ -26,27 +26,27 @@
     srcs: ["src/**/*.java"],
 
     libs: [
-        "android.test.runner",
         "android.test.base",
         "android.test.mock",
+        "android.test.runner",
     ],
     compile_multilib: "both",
 
     static_libs: [
+        "Robolectric_all-target",
+        "androidx.test.ext.junit",
         "androidx.test.rules",
         "compatibility-device-util-axt",
-        "service-appsearch",
-        "truth-prebuilt",
-        "androidx.test.ext.junit",
-        "junit",
         "framework-nearby-pre-jarjar",
         "guava",
-        "robolectric_android-all-stub",
-        "Robolectric_all-target",
+        "junit",
         "libprotobuf-java-lite",
         "mockito-target",
         "platform-test-annotations",
+        "robolectric_android-all-stub",
+        "service-appsearch",
         "service-nearby",
+        "truth-prebuilt",
     ],
     test_suites: [
         "general-tests",