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",