Merge "Add test for progresshandlerbase" into tm-dev
diff --git a/nearby/service/Android.bp b/nearby/service/Android.bp
index 6743c21..12fce04 100644
--- a/nearby/service/Android.bp
+++ b/nearby/service/Android.bp
@@ -67,6 +67,7 @@
"modules-utils-handlerexecutor",
"modules-utils-preconditions",
"modules-utils-backgroundthread",
+ "presence-lite-protos",
],
sdk_version: "system_server_current",
// This is included in service-connectivity which is 30+
diff --git a/nearby/service/java/com/android/server/nearby/NearbyService.java b/nearby/service/java/com/android/server/nearby/NearbyService.java
index 5326673..6d149fc 100644
--- a/nearby/service/java/com/android/server/nearby/NearbyService.java
+++ b/nearby/service/java/com/android/server/nearby/NearbyService.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.hardware.location.ContextHubManager;
import android.nearby.INearbyManager;
import android.nearby.IScanListener;
import android.nearby.ScanRequest;
@@ -33,34 +34,42 @@
import com.android.server.nearby.common.locator.LocatorContextWrapper;
import com.android.server.nearby.fastpair.FastPairManager;
+import com.android.server.nearby.injector.ContextHubManagerAdapter;
import com.android.server.nearby.injector.Injector;
+import com.android.server.nearby.presence.ChreCommunication;
+import com.android.server.nearby.presence.PresenceManager;
import com.android.server.nearby.provider.DiscoveryProviderManager;
import com.android.server.nearby.provider.FastPairDataProvider;
-/**
- * Service implementing nearby functionality.
- */
+import java.util.concurrent.Executors;
+
+import service.proto.Blefilter;
+
+/** Service implementing nearby functionality. */
public class NearbyService extends INearbyManager.Stub {
public static final String TAG = "NearbyService";
private final Context mContext;
private final SystemInjector mSystemInjector;
private final FastPairManager mFastPairManager;
- private final BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- int state = intent
- .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
- if (state == BluetoothAdapter.STATE_ON) {
- if (mSystemInjector != null) {
- // 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.initializeBluetoothAdapter();
+ private final PresenceManager mPresenceManager;
+ private final BroadcastReceiver mBluetoothReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int state =
+ intent.getIntExtra(
+ BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if (state == BluetoothAdapter.STATE_ON) {
+ if (mSystemInjector != null) {
+ // 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.initializeBluetoothAdapter();
+ }
+ }
}
- }
- }
- };
+ };
private DiscoveryProviderManager mProviderManager;
public NearbyService(Context context) {
@@ -69,6 +78,19 @@
mProviderManager = new DiscoveryProviderManager(context, mSystemInjector);
final LocatorContextWrapper lcw = new LocatorContextWrapper(context, null);
mFastPairManager = new FastPairManager(lcw);
+ mPresenceManager =
+ new PresenceManager(
+ mContext,
+ (results) -> {
+ // TODO(b/221082271): hooked with API codes.
+ for (Blefilter.BleFilterResult result : results.getResultList()) {
+ Log.i(
+ TAG,
+ String.format(
+ "received filter result with id: %d",
+ result.getId()));
+ }
+ });
}
@Override
@@ -84,7 +106,7 @@
/**
* Called by the service initializer.
*
- * {@see com.android.server.SystemService#onBootPhase}.
+ * <p>{@see com.android.server.SystemService#onBootPhase}.
*/
public void onBootPhase(int phase) {
switch (phase) {
@@ -95,17 +117,22 @@
case PHASE_BOOT_COMPLETED:
// The nearby service must be functioning after this boot phase.
mSystemInjector.initializeBluetoothAdapter();
- mContext.registerReceiver(mBluetoothReceiver,
+ mContext.registerReceiver(
+ mBluetoothReceiver,
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
mFastPairManager.initiate();
+ mSystemInjector.initializeContextHubManagerAdapter();
+ mPresenceManager.initiate(
+ new ChreCommunication(
+ mSystemInjector, Executors.newSingleThreadExecutor()));
break;
}
}
private static final class SystemInjector implements Injector {
private final Context mContext;
- @Nullable
- private BluetoothAdapter mBluetoothAdapter;
+ @Nullable private BluetoothAdapter mBluetoothAdapter;
+ @Nullable private ContextHubManagerAdapter mContextHubManagerAdapter;
SystemInjector(Context context) {
mContext = context;
@@ -117,6 +144,11 @@
return mBluetoothAdapter;
}
+ @Override
+ public ContextHubManagerAdapter getContextHubManagerAdapter() {
+ return mContextHubManagerAdapter;
+ }
+
synchronized void initializeBluetoothAdapter() {
if (mBluetoothAdapter != null) {
return;
@@ -127,6 +159,17 @@
}
mBluetoothAdapter = manager.getAdapter();
}
- }
+ synchronized void initializeContextHubManagerAdapter() {
+ if (mContextHubManagerAdapter != null) {
+ return;
+ }
+ ContextHubManager manager = mContext.getSystemService(ContextHubManager.class);
+ if (manager == null) {
+ return;
+ }
+ mContextHubManagerAdapter = new ContextHubManagerAdapter(manager);
+ }
+
+ }
}
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
index f41715a..08e98db 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
@@ -79,6 +79,11 @@
Rpcs.GetObservedDeviceResponse response =
FastPairDataProvider.getInstance()
.loadFastPairAntispoofkeyDeviceMetadata(model);
+ if (response == null) {
+ Log.e(TAG, "server does not have model id "
+ + Hex.bytesToStringLowercase(model));
+ return;
+ }
Locator.get(mContext, FastPairHalfSheetManager.class).showHalfSheet(
DataUtils.toScanFastPairStoreItem(
response, mBleAddress,
@@ -128,6 +133,9 @@
static Data.FastPairDeviceWithAccountKey findRecognizedDevice(
List<Data.FastPairDeviceWithAccountKey> devices, BloomFilter bloomFilter, byte[] salt) {
for (Data.FastPairDeviceWithAccountKey device : devices) {
+ if (device.getAccountKey().toByteArray() == null) {
+ continue;
+ }
byte[] rotatedKey = concat(device.getAccountKey().toByteArray(), salt);
if (bloomFilter.possiblyContains(rotatedKey)) {
return device;
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 3a3c962..04f72f0 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
@@ -86,6 +86,7 @@
/** 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";
+ public static final boolean ENFORCED_SCAN_ENABLED_VALUE = false;
private static Executor sFastPairExecutor;
@@ -94,7 +95,7 @@
final LocatorContextWrapper mLocatorContextWrapper;
final IntentFilter mIntentFilter;
final Locator mLocator;
- private boolean mScanEnabled = false;
+ private boolean mScanEnabled = ENFORCED_SCAN_ENABLED_VALUE;
private final BroadcastReceiver mScreenBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -148,6 +149,7 @@
Locator.getFromContextWrapper(mLocatorContextWrapper, FastPairCacheManager.class);
try {
mScanEnabled = getScanEnabledFromSettings();
+ mScanEnabled = ENFORCED_SCAN_ENABLED_VALUE;
} catch (Settings.SettingNotFoundException e) {
Log.w(TAG,
"initiate: Failed to get initial scan enabled status from Settings.", e);
@@ -388,6 +390,7 @@
return;
}
mScanEnabled = scanEnabled;
+ mScanEnabled = ENFORCED_SCAN_ENABLED_VALUE;
invalidateScan();
}
diff --git a/nearby/service/java/com/android/server/nearby/injector/ContextHubManagerAdapter.java b/nearby/service/java/com/android/server/nearby/injector/ContextHubManagerAdapter.java
new file mode 100644
index 0000000..9af0227
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/injector/ContextHubManagerAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * 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.injector;
+
+import android.hardware.location.ContextHubClient;
+import android.hardware.location.ContextHubClientCallback;
+import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubManager;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.NanoAppState;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/** Wrap {@link ContextHubManager} for dependence injection. */
+public class ContextHubManagerAdapter {
+ private final ContextHubManager mManager;
+
+ public ContextHubManagerAdapter(ContextHubManager manager) {
+ mManager = manager;
+ }
+
+ /**
+ * Returns the list of ContextHubInfo objects describing the available Context Hubs.
+ *
+ * @return the list of ContextHubInfo objects
+ * @see ContextHubInfo
+ */
+ public List<ContextHubInfo> getContextHubs() {
+ return mManager.getContextHubs();
+ }
+
+ /**
+ * Requests a query for nanoapps loaded at the specified Context Hub.
+ *
+ * @param hubInfo the hub to query a list of nanoapps from
+ * @return the ContextHubTransaction of the request
+ * @throws NullPointerException if hubInfo is null
+ */
+ public ContextHubTransaction<List<NanoAppState>> queryNanoApps(ContextHubInfo hubInfo) {
+ return mManager.queryNanoApps(hubInfo);
+ }
+
+ /**
+ * Creates and registers a client and its callback with the Context Hub Service.
+ *
+ * <p>A client is registered with the Context Hub Service for a specified Context Hub. When the
+ * registration succeeds, the client can send messages to nanoapps through the returned {@link
+ * ContextHubClient} object, and receive notifications through the provided callback.
+ *
+ * @param hubInfo the hub to attach this client to
+ * @param executor the executor to invoke the callback
+ * @param callback the notification callback to register
+ * @return the registered client object
+ * @throws IllegalArgumentException if hubInfo does not represent a valid hub
+ * @throws IllegalStateException if there were too many registered clients at the service
+ * @throws NullPointerException if callback, hubInfo, or executor is null
+ */
+ public ContextHubClient createClient(
+ ContextHubInfo hubInfo, ContextHubClientCallback callback, Executor executor) {
+ return mManager.createClient(hubInfo, callback, executor);
+ }
+}
diff --git a/nearby/service/java/com/android/server/nearby/injector/Injector.java b/nearby/service/java/com/android/server/nearby/injector/Injector.java
index d221722..f990dc9 100644
--- a/nearby/service/java/com/android/server/nearby/injector/Injector.java
+++ b/nearby/service/java/com/android/server/nearby/injector/Injector.java
@@ -19,14 +19,14 @@
import android.bluetooth.BluetoothAdapter;
/**
- * Nearby dependency injector. To be used for accessing various Nearby class instances and as a
- * handle for mock injection.
+ * Nearby dependency injector. To be used for accessing various Nearby class instances and as a
+ * handle for mock injection.
*/
public interface Injector {
- /**
- * Get the BluetoothAdapter for BleDiscoveryProvider to scan.
- */
+ /** Get the BluetoothAdapter for BleDiscoveryProvider to scan. */
BluetoothAdapter getBluetoothAdapter();
+ /** Get the ContextHubManagerAdapter for ChreDiscoveryProvider to scan. */
+ ContextHubManagerAdapter getContextHubManagerAdapter();
}
diff --git a/nearby/service/java/com/android/server/nearby/presence/ChreCommunication.java b/nearby/service/java/com/android/server/nearby/presence/ChreCommunication.java
new file mode 100644
index 0000000..fc9863e
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/presence/ChreCommunication.java
@@ -0,0 +1,261 @@
+/*
+ * 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.presence;
+
+import android.annotation.Nullable;
+import android.hardware.location.ContextHubClient;
+import android.hardware.location.ContextHubClientCallback;
+import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppState;
+import android.util.Log;
+
+import com.android.server.nearby.injector.ContextHubManagerAdapter;
+import com.android.server.nearby.injector.Injector;
+
+import com.google.common.base.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * Responsible for setting up communication with the appropriate contexthub on the device and
+ * handling nanoapp messages to / from it.
+ */
+public class ChreCommunication extends ContextHubClientCallback {
+
+ /** Callback that receives messages forwarded from the context hub. */
+ public interface ContextHubCommsCallback {
+ /** Indicates whether {@link ChreCommunication} was started successfully. */
+ void started(boolean success);
+
+ /** Indicates the ContextHub has been restarted. */
+ void onHubReset();
+
+ /**
+ * Indicates the given {@code nanoAppId} has been restarted. Either via code download or by
+ * being enabled by CHRE.
+ */
+ void onNanoAppRestart(long nanoAppId);
+
+ /** Indicates a new {@link NanoAppMessage} has been received. */
+ void onMessageFromNanoApp(NanoAppMessage message);
+ }
+
+ public static final String TAG = "PresenceService";
+ @Nullable private final ContextHubManagerAdapter mManager;
+ private final Executor mExecutor;
+
+ private boolean mStarted = false;
+ @Nullable private ContextHubCommsCallback mCallback;
+ @Nullable private ContextHubClient mContextHubClient;
+
+ public ChreCommunication(Injector injector, Executor executor) {
+ this.mManager = injector.getContextHubManagerAdapter();
+ mExecutor = executor;
+ }
+
+ /**
+ * Starts communication with the contexthub. This will invoke {@link
+ * ContextHubCommsCallback#start(boolean)} on completion.
+ *
+ * @param nanoAppIds - List of IDs that must have at least one match inside the chosen
+ * contexthub.
+ */
+ public synchronized void start(ContextHubCommsCallback callback, Set<Long> nanoAppIds) {
+ if (this.mManager == null) {
+ Log.e(TAG, "ContexHub not available in this device");
+ return;
+ } else {
+ Log.i(TAG, "Start ChreCommunication");
+ }
+ Preconditions.checkNotNull(callback);
+ if (nanoAppIds.isEmpty() || mManager == null) {
+ callback.started(false);
+ return;
+ }
+ if (mStarted) {
+ this.mCallback.started(true);
+ return;
+ }
+
+ // Use this to indicate whether stop was called before the transaction below
+ // completes.
+ mStarted = true;
+ this.mCallback = callback;
+
+ List<ContextHubInfo> contextHubs = mManager.getContextHubs();
+
+ // Make a copy of the list so we can modify it during our async callbacks (in case the code
+ // is still iterating)
+ List<ContextHubInfo> validContextHubs = new ArrayList<>(contextHubs);
+
+ for (ContextHubInfo info : contextHubs) {
+ ContextHubTransaction<List<NanoAppState>> transaction = mManager.queryNanoApps(info);
+ Log.i(TAG, "After query Nano Apps ");
+ transaction.setOnCompleteListener(
+ new OnQueryCompleteListener(info, validContextHubs, nanoAppIds), mExecutor);
+ }
+ }
+
+ /**
+ * Closes the connection to the {@link ContextHub} chosen during start.
+ *
+ * <p>NOTE: Do not invoke any other methods on this class after this returns.
+ */
+ public synchronized void stop() {
+ if (!mStarted) {
+ return;
+ }
+ mStarted = false;
+ if (mContextHubClient != null) {
+ mContextHubClient.close();
+ mContextHubClient = null;
+ }
+ }
+
+ /** Sends a {@link NanoAppMessage} to Context Hub Nearby nanoapp. */
+ public synchronized boolean sendMessageToNanoApp(NanoAppMessage message) {
+ if (mContextHubClient == null) {
+ Log.i(TAG, "Error sending message to nanoapp, contextHubClient is null");
+ return false;
+ }
+ int result = mContextHubClient.sendMessageToNanoApp(message);
+ if (result != ContextHubTransaction.RESULT_SUCCESS) {
+ Log.i(
+ TAG,
+ String.format(
+ Locale.getDefault(),
+ "Error sending message to nanoapp: %s",
+ contextHubTransactionResultToString(result)));
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public synchronized void onMessageFromNanoApp(ContextHubClient client, NanoAppMessage message) {
+ mCallback.onMessageFromNanoApp(message);
+ }
+
+ @Override
+ public synchronized void onHubReset(ContextHubClient client) {
+ mCallback.onHubReset();
+ }
+
+ @Override
+ public synchronized void onNanoAppLoaded(ContextHubClient client, long nanoAppId) {
+ Log.i(TAG, String.format("Nanoapp ID loaded: %s", nanoAppId));
+ mCallback.onNanoAppRestart(nanoAppId);
+ }
+
+ private static String contextHubTransactionResultToString(int result) {
+ switch (result) {
+ case ContextHubTransaction.RESULT_SUCCESS:
+ return "RESULT_SUCCESS";
+ case ContextHubTransaction.RESULT_FAILED_UNKNOWN:
+ return "RESULT_FAILED_UNKNOWN";
+ case ContextHubTransaction.RESULT_FAILED_BAD_PARAMS:
+ return "RESULT_FAILED_BAD_PARAMS";
+ case ContextHubTransaction.RESULT_FAILED_UNINITIALIZED:
+ return "RESULT_FAILED_UNINITIALIZED";
+ case ContextHubTransaction.RESULT_FAILED_BUSY:
+ return "RESULT_FAILED_BUSY";
+ case ContextHubTransaction.RESULT_FAILED_AT_HUB:
+ return "RESULT_FAILED_AT_HUB";
+ case ContextHubTransaction.RESULT_FAILED_TIMEOUT:
+ return "RESULT_FAILED_TIMEOUT";
+ case ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE:
+ return "RESULT_FAILED_SERVICE_INTERNAL_FAILURE";
+ case ContextHubTransaction.RESULT_FAILED_HAL_UNAVAILABLE:
+ return "RESULT_FAILED_HAL_UNAVAILABLE";
+ default:
+ return String.format(Locale.getDefault(), "UNKNOWN_RESULT value=%d", result);
+ }
+ }
+
+ /**
+ * Used when initializing the class to identify the appropriate {@link ContextHubInfo} to listen
+ * to.
+ */
+ class OnQueryCompleteListener
+ implements ContextHubTransaction.OnCompleteListener<List<NanoAppState>> {
+
+ private final ContextHubInfo mQueriedContextHub;
+ private final List<ContextHubInfo> mContextHubs;
+ private final Set<Long> mNanoAppIds;
+
+ OnQueryCompleteListener(
+ ContextHubInfo queriedContextHub,
+ List<ContextHubInfo> contextHubs,
+ Set<Long> nanoAppIds) {
+ this.mQueriedContextHub = queriedContextHub;
+ this.mContextHubs = contextHubs;
+ this.mNanoAppIds = nanoAppIds;
+ }
+
+ @Override
+ public void onComplete(
+ ContextHubTransaction<List<NanoAppState>> transaction,
+ ContextHubTransaction.Response<List<NanoAppState>> response) {
+ Log.i(TAG, "query nano app onComplete");
+ // Ensure the class hasn't found a client already or stop hasn't been called before
+ // the transaction completed to avoid messing with state.
+ if (mContextHubClient != null || !mStarted) {
+ return;
+ }
+
+ if (response.getResult() == ContextHubTransaction.RESULT_SUCCESS) {
+ for (NanoAppState state : response.getContents()) {
+ if (mNanoAppIds.contains(state.getNanoAppId())) {
+ Log.i(
+ TAG,
+ String.format(
+ "Found valid contexthub: %s", mQueriedContextHub.getId()));
+ mContextHubClient =
+ mManager.createClient(
+ mQueriedContextHub, ChreCommunication.this, mExecutor);
+ mCallback.started(true);
+ return;
+ }
+ }
+ Log.e(
+ TAG,
+ String.format(
+ "Didn't find the nanoapp on contexthub: %s",
+ mQueriedContextHub.getId()));
+ } else {
+ Log.e(
+ TAG,
+ String.format(
+ "Failed to communicate with contexthub: %s",
+ mQueriedContextHub.getId()));
+ }
+
+ mContextHubs.remove(mQueriedContextHub);
+ // If this is the last context hub response left to receive, indicate that
+ // there isn't a valid context available on this device.
+ if (mContextHubs.isEmpty()) {
+ mCallback.started(false);
+ }
+ }
+ }
+}
diff --git a/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java b/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java
new file mode 100644
index 0000000..66d4864
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java
@@ -0,0 +1,167 @@
+/*
+ * 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.presence;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.location.NanoAppMessage;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import java.util.Collections;
+
+import service.proto.Blefilter;
+
+/** PresenceManager is the class initiated in nearby service to handle presence related work. */
+public class PresenceManager {
+ /** Callback that receives filter results from CHRE Nanoapp. */
+ public interface PresenceCallback {
+ /** Called when {@link BleFilterResults} has been received. */
+ void onFilterResults(Blefilter.BleFilterResults filterResults);
+ }
+
+ private static final String TAG = "PresenceService";
+ // Nanoapp ID reserved for Nearby Presence.
+ /** @hide */
+ @VisibleForTesting
+ public static final long NANOAPP_ID = 0x476f6f676c001031L;
+ /** @hide */
+ @VisibleForTesting
+ public static final int NANOAPP_MESSAGE_TYPE_FILTER = 3;
+ /** @hide */
+ @VisibleForTesting
+ public static final int NANOAPP_MESSAGE_TYPE_FILTER_RESULT = 4;
+ private final Context mContext;
+ private final PresenceCallback mPresenceCallback;
+ private final ChreCallback mChreCallback;
+ private ChreCommunication mChreCommunication;
+
+ private Blefilter.BleFilters mFilters = null;
+ private boolean mChreStarted = false;
+
+ private final IntentFilter mIntentFilter;
+
+ private final BroadcastReceiver mScreenBroadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
+ // TODO(b/221082271): removed this faked data once hooked with API codes.
+ Log.d(TAG, "Update Presence CHRE filter");
+ ByteString mac_addr = ByteString.copyFrom(new byte[] {1, 2, 3, 4, 5, 6});
+ Blefilter.BleFilter filter =
+ Blefilter.BleFilter.newBuilder()
+ .setId(0)
+ .setUuid(0xFCF1)
+ .setIntent(1)
+ .setMacAddress(mac_addr)
+ .build();
+ Blefilter.BleFilters filters =
+ Blefilter.BleFilters.newBuilder().addFilter(filter).build();
+ updateFilters(filters);
+ }
+ }
+ };
+
+ public PresenceManager(Context context, PresenceCallback presenceCallback) {
+ mContext = context;
+ mPresenceCallback = presenceCallback;
+ mChreCallback = new ChreCallback();
+ mIntentFilter = new IntentFilter();
+ }
+
+ /** Function called when nearby service start. */
+ public void initiate(ChreCommunication chreCommunication) {
+ mChreCommunication = chreCommunication;
+ mChreCommunication.start(mChreCallback, Collections.singleton(NANOAPP_ID));
+
+ mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ mContext.registerReceiver(mScreenBroadcastReceiver, mIntentFilter);
+ }
+
+ /** Updates the fitlers in Context Hub. */
+ public synchronized void updateFilters(Blefilter.BleFilters filters) {
+ mFilters = filters;
+ if (mChreStarted) {
+ sendFilters(mFilters);
+ mFilters = null;
+ }
+ }
+
+ private void sendFilters(Blefilter.BleFilters filters) {
+ NanoAppMessage message =
+ NanoAppMessage.createMessageToNanoApp(
+ NANOAPP_ID, NANOAPP_MESSAGE_TYPE_FILTER, filters.toByteArray());
+ if (!mChreCommunication.sendMessageToNanoApp(message)) {
+ Log.e(TAG, "Failed to send filters to CHRE.");
+ }
+ }
+
+ private class ChreCallback implements ChreCommunication.ContextHubCommsCallback {
+
+ @Override
+ public void started(boolean success) {
+ if (success) {
+ synchronized (PresenceManager.this) {
+ Log.i(TAG, "CHRE communication started");
+ mChreStarted = true;
+ if (mFilters != null) {
+ sendFilters(mFilters);
+ mFilters = null;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onHubReset() {
+ // TODO(b/221082271): hooked with upper level codes.
+ Log.i(TAG, "CHRE reset.");
+ }
+
+ @Override
+ public void onNanoAppRestart(long nanoAppId) {
+ // TODO(b/221082271): hooked with upper level codes.
+ Log.i(TAG, String.format("CHRE NanoApp %d restart.", nanoAppId));
+ }
+
+ @Override
+ public void onMessageFromNanoApp(NanoAppMessage message) {
+ if (message.getNanoAppId() != NANOAPP_ID) {
+ Log.e(TAG, "Received message from unknown nano app.");
+ return;
+ }
+ if (message.getMessageType() == NANOAPP_MESSAGE_TYPE_FILTER_RESULT) {
+ try {
+ Blefilter.BleFilterResults results =
+ Blefilter.BleFilterResults.parseFrom(message.getMessageBody());
+ mPresenceCallback.onFilterResults(results);
+ } catch (InvalidProtocolBufferException e) {
+ Log.e(
+ TAG,
+ String.format("Failed to decode the filter result %s", e.toString()));
+ }
+ }
+ }
+ }
+}
diff --git a/nearby/service/proto/Android.bp b/nearby/service/proto/Android.bp
index d8c059e..1b00cf6 100644
--- a/nearby/service/proto/Android.bp
+++ b/nearby/service/proto/Android.bp
@@ -23,10 +23,22 @@
},
sdk_version: "system_current",
min_sdk_version: "30",
- srcs: ["src/*/*.proto"],
+ srcs: ["src/fastpair/*.proto"],
apex_available: [
"com.android.tethering",
],
}
-
+java_library {
+ name: "presence-lite-protos",
+ proto: {
+ type: "lite",
+ canonical_path_from_root: false,
+ },
+ sdk_version: "system_current",
+ min_sdk_version: "30",
+ srcs: ["src/presence/*.proto"],
+ apex_available: [
+ "com.android.tethering",
+ ],
+}
\ No newline at end of file
diff --git a/nearby/service/proto/src/presence/blefilter.proto b/nearby/service/proto/src/presence/blefilter.proto
new file mode 100644
index 0000000..da56522
--- /dev/null
+++ b/nearby/service/proto/src/presence/blefilter.proto
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+// Proto Messages define the interface between Nearby nanoapp and its host.
+//
+// Host registers its interest in BLE event by configuring nanoapp with Filters.
+// The nanoapp keeps watching BLE events and notifies host once an event matches
+// a Filter.
+//
+// Each Filter is defined by its id (required) with optional fields of rssi,
+// uuid, MAC etc. The host should guarantee the uniqueness of ids. It is
+// convenient to assign id incrementally when adding a Filter such that its id
+// is the same as the index of the repeated field in Filters.
+//
+// The nanoapp compares each BLE event against the list of Filters, and notifies
+// host when the event matches a Filter. The Field's id will be sent back to
+// host in the FilterResult.
+//
+// It is possible for the nanoapp to return multiple ids when an event matches
+// multiple Filters.
+
+syntax = "proto2";
+
+package service.proto;
+
+// Certificate to verify BLE events from trusted devices.
+// When receiving an advertisement from a remote device, it will
+// be decrypted by authenticity_key and SHA hashed. The device
+// is verified as trusted if the hash result is equal to
+// metadata_encryption_key_tag.
+// See details in go/ns-certificates.
+message PublicateCertificate {
+ optional bytes authenticity_key = 1;
+ optional bytes metadata_encryption_key_tag = 2;
+}
+
+message BleFilter {
+ optional uint32 id = 1; // Required, unique id of this filter.
+ // Maximum delay to notify the client after an event occurs.
+ optional uint32 latency_ms = 2;
+ optional uint32 uuid = 3;
+ // MAC address of the advertising device.
+ optional bytes mac_address = 4;
+ optional bytes mac_mask = 5;
+ // Represents an action that scanners should take when they receive this
+ // packet. See go/nearby-presence-spec for details.
+ optional uint32 intent = 6;
+ // Notify the client if the advertising device is within the distance.
+ // For moving object, the distance is averaged over data sampled within
+ // the period of latency defined above.
+ optional float distance_m = 7;
+ // Used to verify the list of trusted devices.
+ repeated PublicateCertificate certficate = 8;
+}
+
+message BleFilters {
+ repeated BleFilter filter = 1;
+}
+
+// FilterResult is returned to host when a BLE event matches a Filter.
+message BleFilterResult {
+ optional uint32 id = 1; // id of the matched Filter.
+ // TODO(b/193756395): replace with BLE event proto.
+ optional bytes raw_data = 2;
+}
+
+message BleFilterResults {
+ repeated BleFilterResult result = 1;
+}
diff --git a/nearby/tests/multidevices/clients/Android.bp b/nearby/tests/multidevices/clients/Android.bp
index 72b752b..5e0ca15 100644
--- a/nearby/tests/multidevices/clients/Android.bp
+++ b/nearby/tests/multidevices/clients/Android.bp
@@ -29,6 +29,7 @@
"error_prone_annotations",
"fast-pair-lite-protos",
"framework-annotations-lib",
+ "gson-prebuilt-jar",
"kotlin-stdlib",
"mobly-snippet-lib",
"service-nearby",
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
index add0bc3..6c28b97 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
@@ -42,10 +42,10 @@
@AsyncRpc(description = "Starts scanning as Fast Pair seeker to find Fast Pair provider devices.")
fun startScan(callbackId: String) {
val scanRequest = ScanRequest.Builder()
- .setScanMode(ScanRequest.SCAN_MODE_LOW_LATENCY)
- .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR)
- .setEnableBle(true)
- .build()
+ .setScanMode(ScanRequest.SCAN_MODE_LOW_LATENCY)
+ .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR)
+ .setEnableBle(true)
+ .build()
scanCallback = ScanCallbackEvents(callbackId)
Log.i("Start Fast Pair scanning via BLE...")
@@ -72,6 +72,42 @@
appContext.sendBroadcast(scanIntent)
}
+ /** Puts a model id to FastPairAntiSpoofKeyDeviceMetadata pair into test data cache.
+ *
+ * @param modelId a string of model id to be associated with.
+ * @param json a string of FastPairAntiSpoofKeyDeviceMetadata JSON object.
+ */
+ @Rpc(description = "Puts a model id to FastPairAntiSpoofKeyDeviceMetadata pair into test data cache.")
+ fun putAntiSpoofKeyDeviceMetadata(modelId: String, json: String) {
+ Log.i("Puts a model id to FastPairAntiSpoofKeyDeviceMetadata pair into test data cache.")
+ FastPairTestDataCache.putAntiSpoofKeyDeviceMetadata(modelId, json)
+ }
+
+ /** Puts an array of FastPairAccountKeyDeviceMetadata into test data cache.
+ *
+ * @param json a string of FastPairAccountKeyDeviceMetadata JSON array.
+ */
+ @Rpc(description = "Puts an array of FastPairAccountKeyDeviceMetadata into test data cache.")
+ fun putAccountKeyDeviceMetadata(json: String) {
+ Log.i("Puts an array of FastPairAccountKeyDeviceMetadata into test data cache.")
+ FastPairTestDataCache.putAccountKeyDeviceMetadata(json)
+ }
+
+ /** Dumps all FastPairAccountKeyDeviceMetadata from the test data cache. */
+ @Rpc(description = "Dumps all FastPairAccountKeyDeviceMetadata from the test data cache.")
+ fun dumpAccountKeyDeviceMetadata(): String {
+ Log.i("Dumps all FastPairAccountKeyDeviceMetadata from the test data cache.")
+ return FastPairTestDataCache.dumpAccountKeyDeviceMetadata()
+ }
+
+ /** Invokes when the snippet runner shutting down. */
+ override fun shutdown() {
+ super.shutdown()
+
+ Log.i("Resets the Fast Pair test data cache.")
+ FastPairTestDataCache.reset()
+ }
+
companion object {
private const val FAST_PAIR_MANAGER_ACTION_START_PAIRING = "NEARBY_START_PAIRING"
private const val FAST_PAIR_MANAGER_EXTRA_MODEL_ID = "MODELID"
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataCache.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataCache.kt
new file mode 100644
index 0000000..28523c1
--- /dev/null
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataCache.kt
@@ -0,0 +1,302 @@
+/*
+ * 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 android.nearby.multidevices.fastpair.seeker
+
+import android.nearby.FastPairAccountKeyDeviceMetadata
+import android.nearby.FastPairAntispoofkeyDeviceMetadata
+import android.nearby.FastPairDeviceMetadata
+import android.nearby.FastPairDiscoveryItem
+import com.google.common.io.BaseEncoding.base64
+import com.google.gson.Gson
+import com.google.gson.annotations.SerializedName
+
+/** Manage a cache of Fast Pair test data for testing. */
+object FastPairTestDataCache {
+ private val gson = Gson()
+ val accountKeyDeviceMetadata = mutableListOf<FastPairAccountKeyDeviceMetadata>()
+ val antiSpoofKeyDeviceMetadataMap =
+ mutableMapOf<String, FastPairAntispoofkeyDeviceMetadata>()
+
+ fun putAccountKeyDeviceMetadata(json: String) {
+ accountKeyDeviceMetadata +=
+ gson.fromJson(json, Array<FastPairAccountKeyDeviceMetadataData>::class.java)
+ .map { it.toFastPairAccountKeyDeviceMetadata() }
+ }
+
+ fun dumpAccountKeyDeviceMetadata(): String =
+ gson.toJson(accountKeyDeviceMetadata.map { FastPairAccountKeyDeviceMetadataData(it) })
+
+ fun putAntiSpoofKeyDeviceMetadata(modelId: String, json: String) {
+ antiSpoofKeyDeviceMetadataMap[modelId] =
+ gson.fromJson(json, FastPairAntiSpoofKeyDeviceMetadataData::class.java)
+ .toFastPairAntispoofkeyDeviceMetadata()
+ }
+
+ fun reset() {
+ accountKeyDeviceMetadata.clear()
+ antiSpoofKeyDeviceMetadataMap.clear()
+ }
+
+ data class FastPairAccountKeyDeviceMetadataData(
+ @SerializedName("account_key") val accountKey: String?,
+ @SerializedName("sha256_account_key_public_address") val accountKeyPublicAddress: String?,
+ @SerializedName("fast_pair_device_metadata") val deviceMeta: FastPairDeviceMetadataData?,
+ @SerializedName("fast_pair_discovery_item") val discoveryItem: FastPairDiscoveryItemData?,
+ ) {
+ constructor(meta: FastPairAccountKeyDeviceMetadata) : this(
+ accountKey = meta.accountKey?.base64Encode(),
+ accountKeyPublicAddress = meta.sha256AccountKeyPublicAddress?.base64Encode(),
+ deviceMeta = meta.fastPairDeviceMetadata?.let { FastPairDeviceMetadataData(it) },
+ discoveryItem = meta.fastPairDiscoveryItem?.let { FastPairDiscoveryItemData(it) },
+ )
+
+ fun toFastPairAccountKeyDeviceMetadata(): FastPairAccountKeyDeviceMetadata {
+ return FastPairAccountKeyDeviceMetadata.Builder()
+ .setAccountKey(accountKey?.base64Decode())
+ .setSha256AccountKeyPublicAddress(accountKeyPublicAddress?.base64Decode())
+ .setFastPairDeviceMetadata(deviceMeta?.toFastPairDeviceMetadata())
+ .setFastPairDiscoveryItem(discoveryItem?.toFastPairDiscoveryItem())
+ .build()
+ }
+ }
+
+ data class FastPairAntiSpoofKeyDeviceMetadataData(
+ @SerializedName("anti_spoofing_public_key_str") val antiSpoofPublicKey: String?,
+ @SerializedName("fast_pair_device_metadata") val deviceMeta: FastPairDeviceMetadataData?,
+ ) {
+ fun toFastPairAntispoofkeyDeviceMetadata(): FastPairAntispoofkeyDeviceMetadata {
+ return FastPairAntispoofkeyDeviceMetadata.Builder()
+ .setAntiSpoofPublicKey(antiSpoofPublicKey?.base64Decode())
+ .setFastPairDeviceMetadata(deviceMeta?.toFastPairDeviceMetadata())
+ .build()
+ }
+ }
+
+ data class FastPairDeviceMetadataData(
+ @SerializedName("assistant_setup_half_sheet") val assistantSetupHalfSheet: String?,
+ @SerializedName("assistant_setup_notification") val assistantSetupNotification: String?,
+ @SerializedName("ble_tx_power") val bleTxPower: Int,
+ @SerializedName("confirm_pin_description") val confirmPinDescription: String?,
+ @SerializedName("confirm_pin_title") val confirmPinTitle: String?,
+ @SerializedName("connect_success_companion_app_installed") val compAppInstalled: String?,
+ @SerializedName("connect_success_companion_app_not_installed") val comAppNotIns: String?,
+ @SerializedName("device_type") val deviceType: Int,
+ @SerializedName("download_companion_app_description") val downloadComApp: String?,
+ @SerializedName("fail_connect_go_to_settings_description") val failConnectDes: String?,
+ @SerializedName("fast_pair_tv_connect_device_no_account_description") val accDes: String?,
+ @SerializedName("image_url") val imageUrl: String?,
+ @SerializedName("initial_notification_description") val initNotification: String?,
+ @SerializedName("initial_notification_description_no_account") val initNoAccount: String?,
+ @SerializedName("initial_pairing_description") val initialPairingDescription: String?,
+ @SerializedName("intent_uri") val intentUri: String?,
+ @SerializedName("locale") val locale: String?,
+ @SerializedName("name") val name: String?,
+ @SerializedName("open_companion_app_description") val openCompanionAppDescription: String?,
+ @SerializedName("retroactive_pairing_description") val retroactivePairingDes: String?,
+ @SerializedName("subsequent_pairing_description") val subsequentPairingDescription: String?,
+ @SerializedName("sync_contacts_description") val syncContactsDescription: String?,
+ @SerializedName("sync_contacts_title") val syncContactsTitle: String?,
+ @SerializedName("sync_sms_description") val syncSmsDescription: String?,
+ @SerializedName("sync_sms_title") val syncSmsTitle: String?,
+ @SerializedName("trigger_distance") val triggerDistance: Double,
+ @SerializedName("case_url") val trueWirelessImageUrlCase: String?,
+ @SerializedName("left_bud_url") val trueWirelessImageUrlLeftBud: String?,
+ @SerializedName("right_bud_url") val trueWirelessImageUrlRightBud: String?,
+ @SerializedName("unable_to_connect_description") val unableToConnectDescription: String?,
+ @SerializedName("unable_to_connect_title") val unableToConnectTitle: String?,
+ @SerializedName("update_companion_app_description") val updateCompAppDes: String?,
+ @SerializedName("wait_launch_companion_app_description") val waitLaunchCompApp: String?,
+ ) {
+ constructor(meta: FastPairDeviceMetadata) : this(
+ assistantSetupHalfSheet = meta.assistantSetupHalfSheet,
+ assistantSetupNotification = meta.assistantSetupNotification,
+ bleTxPower = meta.bleTxPower,
+ confirmPinDescription = meta.confirmPinDescription,
+ confirmPinTitle = meta.confirmPinTitle,
+ compAppInstalled = meta.connectSuccessCompanionAppInstalled,
+ comAppNotIns = meta.connectSuccessCompanionAppNotInstalled,
+ deviceType = meta.deviceType,
+ downloadComApp = meta.downloadCompanionAppDescription,
+ failConnectDes = meta.failConnectGoToSettingsDescription,
+ accDes = meta.fastPairTvConnectDeviceNoAccountDescription,
+ imageUrl = meta.imageUrl,
+ initNotification = meta.initialNotificationDescription,
+ initNoAccount = meta.initialNotificationDescriptionNoAccount,
+ initialPairingDescription = meta.initialPairingDescription,
+ intentUri = meta.intentUri,
+ locale = meta.locale,
+ name = meta.name,
+ openCompanionAppDescription = meta.openCompanionAppDescription,
+ retroactivePairingDes = meta.retroactivePairingDescription,
+ subsequentPairingDescription = meta.subsequentPairingDescription,
+ syncContactsDescription = meta.syncContactsDescription,
+ syncContactsTitle = meta.syncContactsTitle,
+ syncSmsDescription = meta.syncSmsDescription,
+ syncSmsTitle = meta.syncSmsTitle,
+ triggerDistance = meta.triggerDistance.toDouble(),
+ trueWirelessImageUrlCase = meta.trueWirelessImageUrlCase,
+ trueWirelessImageUrlLeftBud = meta.trueWirelessImageUrlLeftBud,
+ trueWirelessImageUrlRightBud = meta.trueWirelessImageUrlRightBud,
+ unableToConnectDescription = meta.unableToConnectDescription,
+ unableToConnectTitle = meta.unableToConnectTitle,
+ updateCompAppDes = meta.updateCompanionAppDescription,
+ waitLaunchCompApp = meta.waitLaunchCompanionAppDescription,
+ )
+
+ fun toFastPairDeviceMetadata(): FastPairDeviceMetadata {
+ return FastPairDeviceMetadata.Builder()
+ .setAssistantSetupHalfSheet(assistantSetupHalfSheet)
+ .setAssistantSetupNotification(assistantSetupNotification)
+ .setBleTxPower(bleTxPower)
+ .setConfirmPinDescription(confirmPinDescription)
+ .setConfirmPinTitle(confirmPinTitle)
+ .setConnectSuccessCompanionAppInstalled(compAppInstalled)
+ .setConnectSuccessCompanionAppNotInstalled(comAppNotIns)
+ .setDeviceType(deviceType)
+ .setDownloadCompanionAppDescription(downloadComApp)
+ .setFailConnectGoToSettingsDescription(failConnectDes)
+ .setFastPairTvConnectDeviceNoAccountDescription(accDes)
+ .setImageUrl(imageUrl)
+ .setInitialNotificationDescription(initNotification)
+ .setInitialNotificationDescriptionNoAccount(initNoAccount)
+ .setInitialPairingDescription(initialPairingDescription)
+ .setIntentUri(intentUri)
+ .setLocale(locale)
+ .setName(name)
+ .setOpenCompanionAppDescription(openCompanionAppDescription)
+ .setRetroactivePairingDescription(retroactivePairingDes)
+ .setSubsequentPairingDescription(subsequentPairingDescription)
+ .setSyncContactsDescription(syncContactsDescription)
+ .setSyncContactsTitle(syncContactsTitle)
+ .setSyncSmsDescription(syncSmsDescription)
+ .setSyncSmsTitle(syncSmsTitle)
+ .setTriggerDistance(triggerDistance.toFloat())
+ .setTrueWirelessImageUrlCase(trueWirelessImageUrlCase)
+ .setTrueWirelessImageUrlLeftBud(trueWirelessImageUrlLeftBud)
+ .setTrueWirelessImageUrlRightBud(trueWirelessImageUrlRightBud)
+ .setUnableToConnectDescription(unableToConnectDescription)
+ .setUnableToConnectTitle(unableToConnectTitle)
+ .setUpdateCompanionAppDescription(updateCompAppDes)
+ .setWaitLaunchCompanionAppDescription(waitLaunchCompApp)
+ .build()
+ }
+ }
+
+ data class FastPairDiscoveryItemData(
+ @SerializedName("action_url") val actionUrl: String?,
+ @SerializedName("action_url_type") val actionUrlType: Int,
+ @SerializedName("app_name") val appName: String?,
+ @SerializedName("attachment_type") val attachmentType: Int,
+ @SerializedName("authentication_public_key_secp256r1") val authenticationPublicKey: String?,
+ @SerializedName("ble_record_bytes") val bleRecordBytes: String?,
+ @SerializedName("debug_category") val debugCategory: Int,
+ @SerializedName("debug_message") val debugMessage: String?,
+ @SerializedName("description") val description: String?,
+ @SerializedName("device_name") val deviceName: String?,
+ @SerializedName("display_url") val displayUrl: String?,
+ @SerializedName("entity_id") val entityId: String?,
+ @SerializedName("feature_graphic_url") val featureGraphicUrl: String?,
+ @SerializedName("first_observation_timestamp_millis") val firstObservationMs: Long,
+ @SerializedName("group_id") val groupId: String?,
+ @SerializedName("icon_fife_url") val iconFfeUrl: String?,
+ @SerializedName("icon_png") val iconPng: String?,
+ @SerializedName("id") val id: String?,
+ @SerializedName("last_observation_timestamp_millis") val lastObservationMs: Long,
+ @SerializedName("last_user_experience") val lastUserExperience: Int,
+ @SerializedName("lost_millis") val lostMillis: Long,
+ @SerializedName("mac_address") val macAddress: String?,
+ @SerializedName("package_name") val packageName: String?,
+ @SerializedName("pending_app_install_timestamp_millis") val pendingAppInstallMs: Long,
+ @SerializedName("rssi") val rssi: Int,
+ @SerializedName("state") val state: Int,
+ @SerializedName("title") val title: String?,
+ @SerializedName("trigger_id") val triggerId: String?,
+ @SerializedName("tx_power") val txPower: Int,
+ @SerializedName("type") val type: Int,
+ ) {
+ constructor(item: FastPairDiscoveryItem) : this(
+ actionUrl = item.actionUrl,
+ actionUrlType = item.actionUrlType,
+ appName = item.appName,
+ attachmentType = item.attachmentType,
+ authenticationPublicKey = item.authenticationPublicKeySecp256r1?.base64Encode(),
+ bleRecordBytes = item.bleRecordBytes?.base64Encode(),
+ debugCategory = item.debugCategory,
+ debugMessage = item.debugMessage,
+ description = item.description,
+ deviceName = item.deviceName,
+ displayUrl = item.displayUrl,
+ entityId = item.entityId,
+ featureGraphicUrl = item.featureGraphicUrl,
+ firstObservationMs = item.firstObservationTimestampMillis,
+ groupId = item.groupId,
+ iconFfeUrl = item.iconFfeUrl,
+ iconPng = item.iconPng?.base64Encode(),
+ id = item.id,
+ lastObservationMs = item.lastObservationTimestampMillis,
+ lastUserExperience = item.lastUserExperience,
+ lostMillis = item.lostMillis,
+ macAddress = item.macAddress,
+ packageName = item.packageName,
+ pendingAppInstallMs = item.pendingAppInstallTimestampMillis,
+ rssi = item.rssi,
+ state = item.state,
+ title = item.title,
+ triggerId = item.triggerId,
+ txPower = item.txPower,
+ type = item.type,
+ )
+
+ fun toFastPairDiscoveryItem(): FastPairDiscoveryItem {
+ return FastPairDiscoveryItem.Builder()
+ .setActionUrl(actionUrl)
+ .setActionUrlType(actionUrlType)
+ .setAppName(appName)
+ .setAttachmentType(attachmentType)
+ .setAuthenticationPublicKeySecp256r1(authenticationPublicKey?.base64Decode())
+ .setBleRecordBytes(bleRecordBytes?.base64Decode())
+ .setDebugCategory(debugCategory)
+ .setDebugMessage(debugMessage)
+ .setDescription(description)
+ .setDeviceName(deviceName)
+ .setDisplayUrl(displayUrl)
+ .setEntityId(entityId)
+ .setFeatureGraphicUrl(featureGraphicUrl)
+ .setFirstObservationTimestampMillis(firstObservationMs)
+ .setGroupId(groupId)
+ .setIconFfeUrl(iconFfeUrl)
+ .setIconPng(iconPng?.base64Decode())
+ .setId(id)
+ .setLastObservationTimestampMillis(lastObservationMs)
+ .setLastUserExperience(lastUserExperience)
+ .setLostMillis(lostMillis)
+ .setMacAddress(macAddress)
+ .setPackageName(packageName)
+ .setPendingAppInstallTimestampMillis(pendingAppInstallMs)
+ .setRssi(rssi)
+ .setState(state)
+ .setTitle(title)
+ .setTriggerId(triggerId)
+ .setTxPower(txPower)
+ .setType(type)
+ .build()
+ }
+ }
+
+ private fun String.base64Decode(): ByteArray = base64().decode(this)
+ private fun ByteArray.base64Encode(): String = base64().encode(this)
+}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataProvider.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataProvider.kt
index 5a30fcf..a1f0369 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataProvider.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataProvider.kt
@@ -17,53 +17,11 @@
package android.nearby.multidevices.fastpair.seeker
import android.accounts.Account
-import android.nearby.*
+import android.nearby.FastPairDataProviderBase
+import android.nearby.FastPairEligibleAccount
import android.util.Log
-import service.proto.Cache
-import service.proto.Rpcs.DeviceType
-import java.util.*
class FastPairTestDataProvider : FastPairDataProviderBase(TAG) {
- private val fastPairDeviceMetadata = FastPairDeviceMetadata.Builder()
- .setAssistantSetupHalfSheet(ASSISTANT_SETUP_HALF_SHEET_TEST_CONSTANT)
- .setAssistantSetupNotification(ASSISTANT_SETUP_NOTIFICATION_TEST_CONSTANT)
- .setBleTxPower(BLE_TX_POWER_TEST_CONSTANT)
- .setConfirmPinDescription(CONFIRM_PIN_DESCRIPTION_TEST_CONSTANT)
- .setConfirmPinTitle(CONFIRM_PIN_TITLE_TEST_CONSTANT)
- .setConnectSuccessCompanionAppInstalled(CONNECT_SUCCESS_COMPANION_APP_INSTALLED_TEST_CONSTANT)
- .setConnectSuccessCompanionAppNotInstalled(CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED_TEST_CONSTANT)
- .setDeviceType(DEVICE_TYPE_HEAD_PHONES_TEST_CONSTANT)
- .setDownloadCompanionAppDescription(DOWNLOAD_COMPANION_APP_DESCRIPTION_TEST_CONSTANT)
- .setFailConnectGoToSettingsDescription(FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION_TEST_CONSTANT)
- .setFastPairTvConnectDeviceNoAccountDescription(TV_CONNECT_DEVICE_NO_ACCOUNT_DESCRIPTION_TEST_CONSTANT)
- .setInitialNotificationDescription(INITIAL_NOTIFICATION_DESCRIPTION_TEST_CONSTANT)
- .setInitialNotificationDescriptionNoAccount(INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT_TEST_CONSTANT)
- .setInitialPairingDescription(INITIAL_PAIRING_DESCRIPTION_TEST_CONSTANT)
- .setLocale(LOCALE_US_LANGUAGE_TEST_CONSTANT)
- .setImage(IMAGE_BYTE_ARRAY_FAKE_TEST_CONSTANT)
- .setImageUrl(IMAGE_URL_TEST_CONSTANT)
- .setIntentUri(
- generateCompanionAppLaunchIntentUri(
- companionAppPackageName = COMPANION_APP_PACKAGE_TEST_CONSTANT,
- activityName = COMPANION_APP_ACTIVITY_TEST_CONSTANT,
- )
- )
- .setOpenCompanionAppDescription(OPEN_COMPANION_APP_DESCRIPTION_TEST_CONSTANT)
- .setRetroactivePairingDescription(RETRO_ACTIVE_PAIRING_DESCRIPTION_TEST_CONSTANT)
- .setSubsequentPairingDescription(SUBSEQUENT_PAIRING_DESCRIPTION_TEST_CONSTANT)
- .setSyncContactsDescription(SYNC_CONTACT_DESCRIPTION_TEST_CONSTANT)
- .setSyncContactsTitle(SYNC_CONTACTS_TITLE_TEST_CONSTANT)
- .setSyncSmsDescription(SYNC_SMS_DESCRIPTION_TEST_CONSTANT)
- .setSyncSmsTitle(SYNC_SMS_TITLE_TEST_CONSTANT)
- .setTriggerDistance(TRIGGER_DISTANCE_TEST_CONSTANT)
- .setTrueWirelessImageUrlCase(TRUE_WIRELESS_IMAGE_URL_CASE_TEST_CONSTANT)
- .setTrueWirelessImageUrlLeftBud(TRUE_WIRELESS_IMAGE_URL_LEFT_BUD_TEST_CONSTANT)
- .setTrueWirelessImageUrlRightBud(TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD_TEST_CONSTANT)
- .setUnableToConnectDescription(UNABLE_TO_CONNECT_DESCRIPTION_TEST_CONSTANT)
- .setUnableToConnectTitle(UNABLE_TO_CONNECT_TITLE_TEST_CONSTANT)
- .setUpdateCompanionAppDescription(UPDATE_COMPANION_APP_DESCRIPTION_TEST_CONSTANT)
- .setWaitLaunchCompanionAppDescription(WAIT_LAUNCH_COMPANION_APP_DESCRIPTION_TEST_CONSTANT)
- .build()
override fun onLoadFastPairAntispoofkeyDeviceMetadata(
request: FastPairAntispoofkeyDeviceMetadataRequest,
@@ -73,12 +31,12 @@
Log.d(TAG, "onLoadFastPairAntispoofkeyDeviceMetadata(modelId: $requestedModelId)")
val fastPairAntiSpoofKeyDeviceMetadata =
- FastPairAntispoofkeyDeviceMetadata.Builder()
- .setAntiSpoofPublicKey(ANTI_SPOOF_PUBLIC_KEY_BYTE_ARRAY)
- .setFastPairDeviceMetadata(fastPairDeviceMetadata)
- .build()
-
- callback.onFastPairAntispoofkeyDeviceMetadataReceived(fastPairAntiSpoofKeyDeviceMetadata)
+ FastPairTestDataCache.antiSpoofKeyDeviceMetadataMap[requestedModelId]
+ if (fastPairAntiSpoofKeyDeviceMetadata != null) {
+ callback.onFastPairAntispoofkeyDeviceMetadataReceived(fastPairAntiSpoofKeyDeviceMetadata)
+ } else {
+ callback.onError(ERROR_CODE_BAD_REQUEST, "No metadata available for $requestedModelId")
+ }
}
override fun onLoadFastPairAccountDevicesMetadata(
@@ -87,48 +45,11 @@
) {
val requestedAccount = request.account
Log.d(TAG, "onLoadFastPairAccountDevicesMetadata(account: $requestedAccount)")
- val discoveryItem = FastPairDiscoveryItem.Builder()
- .setActionUrl(ACTION_URL_TEST_CONSTANT)
- .setActionUrlType(ACTION_URL_TYPE_TEST_CONSTANT)
- .setAppName(APP_NAME_TEST_CONSTANT)
- .setAttachmentType(ATTACHMENT_TYPE_TEST_CONSTANT)
- .setAuthenticationPublicKeySecp256r1(AUTHENTICATION_PUBLIC_KEY_SEC_P256R1_TEST_CONSTANT)
- .setBleRecordBytes(BLE_RECORD_BYTES_TEST_CONSTANT)
- .setDebugCategory(DEBUG_CATEGORY_TEST_CONSTANT)
- .setDebugMessage(DEBUG_MESSAGE_TEST_CONSTANT)
- .setDescription(DESCRIPTION_TEST_CONSTANT)
- .setDeviceName(DEVICE_NAME_TEST_CONSTANT)
- .setDisplayUrl(DISPLAY_URL_TEST_CONSTANT)
- .setEntityId(ENTITY_ID_TEST_CONSTANT)
- .setFeatureGraphicUrl(FEATURE_GRAPHIC_URL_TEST_CONSTANT)
- .setFirstObservationTimestampMillis(FIRST_OBSERVATION_TIMESTAMP_MILLIS_TEST_CONSTANT)
- .setGroupId(GROUP_ID_TEST_CONSTANT)
- .setIconFfeUrl(ICON_FIFE_URL_TEST_CONSTANT)
- .setIconPng(ICON_PNG_TEST_CONSTANT)
- .setId(ID_TEST_CONSTANT)
- .setLastObservationTimestampMillis(LAST_OBSERVATION_TIMESTAMP_MILLIS_TEST_CONSTANT)
- .setLastUserExperience(LAST_USER_EXPERIENCE_TEST_CONSTANT)
- .setLostMillis(LOST_MILLIS_TEST_CONSTANT)
- .setMacAddress(MAC_ADDRESS_TEST_CONSTANT)
- .setPackageName(PACKAGE_NAME_TEST_CONSTANT)
- .setPendingAppInstallTimestampMillis(PENDING_APP_INSTALL_TIMESTAMP_MILLIS_TEST_CONSTANT)
- .setRssi(RSSI_TEST_CONSTANT)
- .setState(STATE_TEST_CONSTANT)
- .setTitle(TITLE_TEST_CONSTANT)
- .setTriggerId(TRIGGER_ID_TEST_CONSTANT)
- .setTxPower(TX_POWER_TEST_CONSTANT)
- .setType(TYPE_TEST_CONSTANT)
- .build()
- val accountDevicesMetadataList = listOf(
- FastPairAccountKeyDeviceMetadata.Builder()
- .setAccountKey(ACCOUNT_KEY_TEST_CONSTANT)
- .setFastPairDeviceMetadata(fastPairDeviceMetadata)
- .setSha256AccountKeyPublicAddress(SHA256_ACCOUNT_KEY_PUBLIC_ADDRESS_TEST_CONSTANT)
- .setFastPairDiscoveryItem(discoveryItem)
- .build()
- )
+ Log.d(TAG, FastPairTestDataCache.dumpAccountKeyDeviceMetadata())
- callback.onFastPairAccountDevicesMetadataReceived(accountDevicesMetadataList)
+ callback.onFastPairAccountDevicesMetadataReceived(
+ FastPairTestDataCache.accountKeyDeviceMetadata
+ )
}
override fun onLoadFastPairEligibleAccounts(
@@ -154,128 +75,32 @@
) {
val requestedAccount = request.account
val requestType = request.requestType
+ val requestTypeString = if (requestType == MANAGE_REQUEST_ADD) "Add" else "Remove"
val requestedBleAddress = request.bleAddress
val requestedAccountKeyDeviceMetadata = request.accountKeyDeviceMetadata
- Log.d(TAG, "onManageFastPairAccountDevice(requestedAccount: $requestedAccount, requestType: $requestType,")
+ Log.d(
+ TAG,
+ "onManageFastPairAccountDevice(requestedAccount: $requestedAccount, " +
+ "requestType: $requestTypeString,"
+ )
Log.d(TAG, "requestedBleAddress: $requestedBleAddress,")
Log.d(TAG, "requestedAccountKeyDeviceMetadata: $requestedAccountKeyDeviceMetadata)")
+ FastPairTestDataCache.accountKeyDeviceMetadata += requestedAccountKeyDeviceMetadata
+
callback.onSuccess()
}
companion object {
private const val TAG = "FastPairTestDataProvider"
-
- private const val BLE_TX_POWER_TEST_CONSTANT = 5
- private const val TRIGGER_DISTANCE_TEST_CONSTANT = 10f
- private const val ACTION_URL_TEST_CONSTANT = "ACTION_URL_TEST_CONSTANT"
- private const val ACTION_URL_TYPE_TEST_CONSTANT = Cache.ResolvedUrlType.APP_VALUE
- private const val APP_NAME_TEST_CONSTANT = "Nearby Mainline Mobly Test Snippet"
- private const val ATTACHMENT_TYPE_TEST_CONSTANT =
- Cache.DiscoveryAttachmentType.DISCOVERY_ATTACHMENT_TYPE_NORMAL_VALUE
- private const val DEBUG_CATEGORY_TEST_CONSTANT =
- Cache.StoredDiscoveryItem.DebugMessageCategory.STATUS_VALID_NOTIFICATION_VALUE
- private const val DEBUG_MESSAGE_TEST_CONSTANT = "DEBUG_MESSAGE_TEST_CONSTANT"
- private const val DESCRIPTION_TEST_CONSTANT = "DESCRIPTION_TEST_CONSTANT"
- private const val DEVICE_NAME_TEST_CONSTANT = "Fast Pair Headphone Simulator"
- private const val DISPLAY_URL_TEST_CONSTANT = "DISPLAY_URL_TEST_CONSTANT"
- private const val ENTITY_ID_TEST_CONSTANT = "ENTITY_ID_TEST_CONSTANT"
- private const val FEATURE_GRAPHIC_URL_TEST_CONSTANT = "FEATURE_GRAPHIC_URL_TEST_CONSTANT"
- private const val FIRST_OBSERVATION_TIMESTAMP_MILLIS_TEST_CONSTANT = 8_393L
- private const val GROUP_ID_TEST_CONSTANT = "GROUP_ID_TEST_CONSTANT"
- private const val ICON_FIFE_URL_TEST_CONSTANT = "ICON_FIFE_URL_TEST_CONSTANT"
- private const val ID_TEST_CONSTANT = "ID_TEST_CONSTANT"
- private const val LAST_OBSERVATION_TIMESTAMP_MILLIS_TEST_CONSTANT = 934_234L
- private const val LAST_USER_EXPERIENCE_TEST_CONSTANT =
- Cache.StoredDiscoveryItem.ExperienceType.EXPERIENCE_GOOD_VALUE
- private const val LOST_MILLIS_TEST_CONSTANT = 393_284L
- private const val MAC_ADDRESS_TEST_CONSTANT = "11:aa:22:bb:34:cd"
- private const val PACKAGE_NAME_TEST_CONSTANT = "android.nearby.package.name.test.constant"
- private const val PENDING_APP_INSTALL_TIMESTAMP_MILLIS_TEST_CONSTANT = 832_393L
- private const val RSSI_TEST_CONSTANT = 9
- private const val STATE_TEST_CONSTANT = Cache.StoredDiscoveryItem.State.STATE_ENABLED_VALUE
- private const val TITLE_TEST_CONSTANT = "TITLE_TEST_CONSTANT"
- private const val TRIGGER_ID_TEST_CONSTANT = "TRIGGER_ID_TEST_CONSTANT"
- private const val TX_POWER_TEST_CONSTANT = 62
- private const val TYPE_TEST_CONSTANT = Cache.NearbyType.NEARBY_DEVICE_VALUE
-
- private val ANTI_SPOOF_PUBLIC_KEY_BYTE_ARRAY =
- "Cbj9eCJrTdDgSYxLkqtfADQi86vIaMvxJsQ298sZYWE=".toByteArray()
- private val LOCALE_US_LANGUAGE_TEST_CONSTANT = Locale.US.language
- private val IMAGE_BYTE_ARRAY_FAKE_TEST_CONSTANT = byteArrayOf(7, 9)
- private val IMAGE_URL_TEST_CONSTANT =
- "2l9cq8LFjK4D7EvPiFAq08DMpUA1b2SoPv9FPw3q6iiwjDvh-hLfKsPCFy0j36rfjDNjSULvRgOodRDxfRHHxA".toFifeUrlString()
- private val TRUE_WIRELESS_IMAGE_URL_CASE_TEST_CONSTANT =
- "oNv4-sFfa0tM1uA7vZ8r7UJPBV8OreiKOFl-_KlFwrqnDD7MoOV4uX8NwGUdYb1dcMm7cfwjZ04628WTeS40".toFifeUrlString()
- private val TRUE_WIRELESS_IMAGE_URL_LEFT_BUD_TEST_CONSTANT =
- "RcGxVZRObx9Avn9AHwSMM4WvDbVNyYlqigW7PlDHL4RLU8W9lcENDMyaTWM9O7JIu1ewSX-FIe_GkQfDlItQkg".toFifeUrlString()
- private val TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD_TEST_CONSTANT =
- "S7AuFqmr_hEqEFo_qfjxAiPz9moae0dkXUSUJV4gVFcysYpn4C95P77egPnuu35C3Eh_UY6_yNpQkmmUqn4N".toFifeUrlString()
private val ELIGIBLE_ACCOUNTS_TEST_CONSTANT = listOf(
FastPairEligibleAccount.Builder()
- .setAccount(Account("nearby-mainline-fpseeker@google.com", "TestAccount"))
+ .setAccount(Account("nearby-mainline-fpseeker@google.com", "FakeTestAccount"))
.setOptIn(true)
.build(),
)
- private val ACCOUNT_KEY_TEST_CONSTANT = byteArrayOf(3)
- private val SHA256_ACCOUNT_KEY_PUBLIC_ADDRESS_TEST_CONSTANT = byteArrayOf(2, 8)
- private val AUTHENTICATION_PUBLIC_KEY_SEC_P256R1_TEST_CONSTANT = byteArrayOf(5, 7)
- private val BLE_RECORD_BYTES_TEST_CONSTANT = byteArrayOf(2, 4)
- private val ICON_PNG_TEST_CONSTANT = byteArrayOf(2, 5)
-
- private const val DEVICE_TYPE_HEAD_PHONES_TEST_CONSTANT = DeviceType.HEADPHONES_VALUE
- private const val ASSISTANT_SETUP_HALF_SHEET_TEST_CONSTANT =
- "This is a test description in half sheet to ask user setup google assistant."
- private const val ASSISTANT_SETUP_NOTIFICATION_TEST_CONSTANT =
- "This is a test description in notification to ask user setup google assistant."
- private const val CONFIRM_PIN_DESCRIPTION_TEST_CONSTANT =
- "Please confirm the pin code to fast pair your device."
- private const val CONFIRM_PIN_TITLE_TEST_CONSTANT = "PIN code confirmation"
- private const val CONNECT_SUCCESS_COMPANION_APP_INSTALLED_TEST_CONSTANT =
- "This is a test description that let user open the companion app."
- private const val CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED_TEST_CONSTANT =
- "This is a test description that let user download the companion app."
- private const val DOWNLOAD_COMPANION_APP_DESCRIPTION_TEST_CONSTANT =
- "This is a test description for downloading companion app."
- private const val FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION_TEST_CONSTANT = "This is a " +
- "test description that indicates go to bluetooth settings when connection fail."
- private const val TV_CONNECT_DEVICE_NO_ACCOUNT_DESCRIPTION_TEST_CONSTANT =
- "This is a test description of the connect device action on TV, " +
- "when user is not logged in."
- private const val INITIAL_NOTIFICATION_DESCRIPTION_TEST_CONSTANT =
- "This is a test description for initial notification."
- private const val INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT_TEST_CONSTANT = "This is a " +
- "test description of initial notification description when account is not present."
- private const val INITIAL_PAIRING_DESCRIPTION_TEST_CONSTANT =
- "This is a test description for Fast Pair initial pairing."
- private const val COMPANION_APP_PACKAGE_TEST_CONSTANT = "android.nearby.companion"
- private const val COMPANION_APP_ACTIVITY_TEST_CONSTANT =
- "android.nearby.companion.MainActivity"
- private const val OPEN_COMPANION_APP_DESCRIPTION_TEST_CONSTANT =
- "This is a test description for opening companion app."
- private const val RETRO_ACTIVE_PAIRING_DESCRIPTION_TEST_CONSTANT =
- "This is a test description that reminds users opt in their device."
- private const val SUBSEQUENT_PAIRING_DESCRIPTION_TEST_CONSTANT =
- "This is a test description that reminds user there is a paired device nearby."
- private const val SYNC_CONTACT_DESCRIPTION_TEST_CONSTANT =
- "This is a test description of the UI to ask the user to confirm to sync contacts."
- private const val SYNC_CONTACTS_TITLE_TEST_CONSTANT = "Sync contacts to your device"
- private const val SYNC_SMS_DESCRIPTION_TEST_CONSTANT =
- "This is a test description of the UI to ask the user to confirm to sync SMS."
- private const val SYNC_SMS_TITLE_TEST_CONSTANT = "Sync SMS to your device"
- private const val UNABLE_TO_CONNECT_DESCRIPTION_TEST_CONSTANT =
- "This is a test description when Fast Pair device is unable to be connected to."
- private const val UNABLE_TO_CONNECT_TITLE_TEST_CONSTANT =
- "Unable to connect your Fast Pair device"
- private const val UPDATE_COMPANION_APP_DESCRIPTION_TEST_CONSTANT =
- "This is a test description for updating companion app."
- private const val WAIT_LAUNCH_COMPANION_APP_DESCRIPTION_TEST_CONSTANT =
- "This is a test description that indicates companion app is about to launch."
private fun ByteArray.bytesToStringLowerCase(): String =
joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
-
- // Primary image serving domain for Google Photos and most other clients of FIFE.
- private fun String.toFifeUrlString() = "https://lh3.googleusercontent.com/$this"
}
}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerConfig.java b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerConfig.java
index 132e026..9fa54fe 100644
--- a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerConfig.java
+++ b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerConfig.java
@@ -4,14 +4,17 @@
import android.annotation.TargetApi;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
+
+import androidx.annotation.Nullable;
+
import com.android.server.nearby.common.bluetooth.BluetoothConsts;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
-import javax.annotation.Nullable;
/** Configuration of a GATT server. */
@TargetApi(18)
diff --git a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerConnection.java b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerConnection.java
index df1a832..b67e00e 100644
--- a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerConnection.java
+++ b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerConnection.java
@@ -5,6 +5,9 @@
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.util.Log;
+
+import androidx.annotation.Nullable;
+
import com.android.server.nearby.common.bluetooth.BluetoothException;
import com.android.server.nearby.common.bluetooth.BluetoothGattException;
import com.android.server.nearby.common.bluetooth.ReservedUuids;
@@ -12,12 +15,12 @@
import com.android.server.nearby.common.bluetooth.util.BluetoothGattUtils;
import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor;
import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.Operation;
+
import com.google.common.annotations.VisibleForTesting;
-// import com.google.common.annotations.VisibleForTesting.Visibility;
-import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.io.BaseEncoding;
+
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
@@ -29,7 +32,6 @@
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
-import javax.annotation.Nullable;
/**
* Connection to a bluetooth LE device over Gatt.
diff --git a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerHelper.java b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerHelper.java
index 3cec24a..0ba96c0 100644
--- a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerHelper.java
+++ b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServerHelper.java
@@ -9,8 +9,10 @@
import android.content.Context;
import android.os.Build.VERSION_CODES;
import android.util.Log;
+
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.server.nearby.common.bluetooth.BluetoothException;
import com.android.server.nearby.common.bluetooth.BluetoothGattException;
import com.android.server.nearby.common.bluetooth.testability.VersionProvider;
@@ -21,12 +23,12 @@
import com.android.server.nearby.common.bluetooth.util.BluetoothGattUtils;
import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor;
import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.Operation;
-// import com.google.common.annotations.VisibleForTesting;
+
import com.google.common.base.Preconditions;
+
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
-import javax.annotation.Nullable;
/**
* Helper for simplifying operations on {@link BluetoothGattServer}.
diff --git a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServlet.java b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServlet.java
index 4c8499c..c4a2473 100644
--- a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServlet.java
+++ b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/gatt/server/BluetoothGattServlet.java
@@ -4,6 +4,7 @@
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
+
import com.android.server.nearby.common.bluetooth.BluetoothGattException;
import com.android.server.nearby.common.bluetooth.gatt.server.BluetoothGattServerConnection.Notifier;
diff --git a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothManager.java b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothManager.java
index 9f47426..4ccfb59 100644
--- a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothManager.java
+++ b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothManager.java
@@ -17,9 +17,11 @@
package com.android.server.nearby.common.bluetooth.testability.android.bluetooth;
import android.content.Context;
+
+import androidx.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.List;
-import javax.annotation.Nullable;
/**
* Mockable wrapper of {@link android.bluetooth.BluetoothManager}.
@@ -37,13 +39,15 @@
* android.bluetooth.BluetoothGattServerCallback)}.
*/
@Nullable
- public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback) {
- return BluetoothGattServer.wrap(mWrappedInstance.openGattServer(context, callback.unwrap()));
+ public BluetoothGattServer openGattServer(Context context,
+ BluetoothGattServerCallback callback) {
+ return BluetoothGattServer.wrap(
+ mWrappedInstance.openGattServer(context, callback.unwrap()));
}
/**
* See {@link android.bluetooth.BluetoothManager#getConnectionState(
- * android.bluetooth.BluetoothDevice, int)}.
+ *android.bluetooth.BluetoothDevice, int)}.
*/
public int getConnectionState(BluetoothDevice device, int profile) {
return mWrappedInstance.getConnectionState(device.unwrap(), profile);
@@ -51,7 +55,8 @@
/** See {@link android.bluetooth.BluetoothManager#getConnectedDevices(int)}. */
public List<BluetoothDevice> getConnectedDevices(int profile) {
- List<android.bluetooth.BluetoothDevice> devices = mWrappedInstance.getConnectedDevices(profile);
+ List<android.bluetooth.BluetoothDevice> devices = mWrappedInstance.getConnectedDevices(
+ profile);
List<BluetoothDevice> wrappedDevices = new ArrayList<>(devices.size());
for (android.bluetooth.BluetoothDevice device : devices) {
wrappedDevices.add(BluetoothDevice.wrap(device));
diff --git a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/util/BluetoothGattUtils.java b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/util/BluetoothGattUtils.java
index b8e1b08..e90b732 100644
--- a/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/util/BluetoothGattUtils.java
+++ b/nearby/tests/multidevices/clients/src/com/android/server/nearby/common/bluetooth/util/BluetoothGattUtils.java
@@ -20,12 +20,14 @@
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
+
+import androidx.annotation.Nullable;
+
import com.android.server.nearby.common.bluetooth.BluetoothException;
+
import java.lang.reflect.Field;
import java.util.Arrays;
-import javax.annotation.Nullable;
-
/**
* Utils for Gatt profile.
*/
@@ -66,12 +68,14 @@
/** Clones a {@link BluetoothGattCharacteristic} so the value can be changed thread-safely. */
public static BluetoothGattCharacteristic clone(BluetoothGattCharacteristic characteristic)
throws BluetoothException {
- BluetoothGattCharacteristic result = new BluetoothGattCharacteristic(characteristic.getUuid(),
+ BluetoothGattCharacteristic result = new BluetoothGattCharacteristic(
+ characteristic.getUuid(),
characteristic.getProperties(), characteristic.getPermissions());
try {
Field instanceIdField = BluetoothGattCharacteristic.class.getDeclaredField("mInstance");
Field serviceField = BluetoothGattCharacteristic.class.getDeclaredField("mService");
- Field descriptorField = BluetoothGattCharacteristic.class.getDeclaredField("mDescriptors");
+ Field descriptorField = BluetoothGattCharacteristic.class.getDeclaredField(
+ "mDescriptors");
instanceIdField.setAccessible(true);
serviceField.setAccessible(true);
descriptorField.setAccessible(true);
diff --git a/nearby/tests/multidevices/host/test_data/fastpair/simulator_antispoofkey_devicemeta_json.txt b/nearby/tests/multidevices/host/test_data/fastpair/simulator_antispoofkey_devicemeta_json.txt
new file mode 100644
index 0000000..7cb29f4
--- /dev/null
+++ b/nearby/tests/multidevices/host/test_data/fastpair/simulator_antispoofkey_devicemeta_json.txt
@@ -0,0 +1,38 @@
+{
+ "anti_spoofing_public_key_str": "sjp\/AOS7+VnTCaueeWorjdeJ8Nc32EOmpe\/QRhzY9+cMNELU1QA3jzgvUXdWW73nl6+EN01eXtLBu2Fw9CGmfA==",
+ "fast_pair_device_metadata": {
+ "assistant_setup_half_sheet": "Get hands-free help on the go from Google Assistant",
+ "assistant_setup_notification": "Tap to set up your Google Assistant",
+ "ble_tx_power": -10,
+ "case_url": "https://lh3.googleusercontent.com/mNZ7CGplQSpZhoY79jXDQU4B65eY2f0SndnYZLk1PSm8zKTYeRU7REmrLL_pptD6HpVI2F_oQ6xhhtZKOvB8EQ",
+ "confirm_pin_description": "",
+ "confirm_pin_title": "",
+ "connect_success_companion_app_installed": "Your device is ready to be set up",
+ "connect_success_companion_app_not_installed": "Download the device app on Google Play to see all available features",
+ "device_type": 7,
+ "download_companion_app_description": "Tap to download device app on Google Play and see all features",
+ "fail_connect_go_to_settings_description": "Try manually pairing to the device by going to Settings",
+ "fast_pair_tv_connect_device_no_account_description": "Select connect to pair your %s with this device",
+ "image_url": "https://lh3.googleusercontent.com/THpAzISZGa5F86cMsBcTPhRWefBPc5dorBxWdOPCGvbFg6ZMHUjFuE-4kbLuoLoIMHf3Fd8jUvvcxnjp_Q",
+ "initial_notification_description": "Tap to pair. Earbuds will be tied to %s",
+ "initial_notification_description_no_account": "Tap to pair with this device",
+ "initial_pairing_description": "%s will appear on devices linked with %s",
+ "intent_uri": "intent:#Intent;action=com.google.android.gms.nearby.discovery%3AACTION_MAGIC_PAIR;package=com.google.android.gms;component=com.google.android.gms/.nearby.discovery.service.DiscoveryService;S.com.google.android.gms.nearby.discovery%3AEXTRA_COMPANION_APP=com.google.android.testapp;end",
+ "left_bud_url": "https://lh3.googleusercontent.com/O8SVJ5E7CXUkpkym7ibZbp6wypuO7HaTFcslT_FjmEzJX4KHoIY_kzLTdK2kwJXiDBgg8cC__sG-JJ5aVnQtFjQ",
+ "locale": "en-US",
+ "name": "Fast Pair Provider Simulator",
+ "open_companion_app_description": "Tap to finish setup",
+ "retroactive_pairing_description": "Save device to %s for faster pairing to your other devices",
+ "right_bud_url": "https://lh3.googleusercontent.com/X_FsRmEKH_fgKzvopyrlyWJAdczRel42Tih7p9-e-U48gBTaggGVQx70K27TzlqIaqYVuaNpTnGoUsKIgiy4WA",
+ "subsequent_pairing_description": "Connect %s to this phone",
+ "sync_contacts_description": "",
+ "sync_contacts_title": "",
+ "sync_sms_description": "",
+ "sync_sms_title": "",
+ "trigger_distance": 0.6000000238418579,
+ "unable_to_connect_description": "Try manually pairing to the device",
+ "unable_to_connect_title": "Unable to connect",
+ "update_companion_app_description": "Tap to update device settings and finish setup",
+ "wait_launch_companion_app_description": "This will take a few moments"
+ }
+}
\ No newline at end of file
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/ChreCommunicationTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/ChreCommunicationTest.java
new file mode 100644
index 0000000..b221f46
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/ChreCommunicationTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.presence;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.location.ContextHubClient;
+import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppState;
+
+import com.android.server.nearby.injector.ContextHubManagerAdapter;
+import com.android.server.nearby.injector.Injector;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class ChreCommunicationTest {
+ @Mock Injector mInjector;
+ @Mock ContextHubManagerAdapter mManager;
+ @Mock ContextHubTransaction<List<NanoAppState>> mTransaction;
+ @Mock ContextHubTransaction.Response<List<NanoAppState>> mTransactionResponse;
+ @Mock ContextHubClient mClient;
+ @Mock ChreCommunication.ContextHubCommsCallback mChreCallback;
+
+ @Captor
+ ArgumentCaptor<ChreCommunication.OnQueryCompleteListener> mOnQueryCompleteListenerCaptor;
+
+ private ChreCommunication mChreCommunication;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mInjector.getContextHubManagerAdapter()).thenReturn(mManager);
+ when(mManager.getContextHubs()).thenReturn(Collections.singletonList(new ContextHubInfo()));
+ when(mManager.queryNanoApps(any())).thenReturn(mTransaction);
+ when(mManager.createClient(any(), any(), any())).thenReturn(mClient);
+ when(mTransactionResponse.getResult()).thenReturn(ContextHubTransaction.RESULT_SUCCESS);
+ when(mTransactionResponse.getContents())
+ .thenReturn(
+ Collections.singletonList(
+ new NanoAppState(PresenceManager.NANOAPP_ID, 1, true)));
+
+ mChreCommunication = new ChreCommunication(mInjector, new InlineExecutor());
+ mChreCommunication.start(mChreCallback, Collections.singleton(PresenceManager.NANOAPP_ID));
+
+ verify(mTransaction).setOnCompleteListener(mOnQueryCompleteListenerCaptor.capture(), any());
+ mOnQueryCompleteListenerCaptor.getValue().onComplete(mTransaction, mTransactionResponse);
+ }
+
+ @Test
+ public void testStart() {
+ verify(mChreCallback).started(true);
+ }
+
+ @Test
+ public void testStop() {
+ mChreCommunication.stop();
+ verify(mClient).close();
+ }
+
+ @Test
+ public void testSendMessageToNanApp() {
+ NanoAppMessage message =
+ NanoAppMessage.createMessageToNanoApp(
+ PresenceManager.NANOAPP_ID,
+ PresenceManager.NANOAPP_MESSAGE_TYPE_FILTER,
+ new byte[] {1, 2, 3});
+ mChreCommunication.sendMessageToNanoApp(message);
+ verify(mClient).sendMessageToNanoApp(eq(message));
+ }
+
+ @Test
+ public void testOnMessageFromNanoApp() {
+ NanoAppMessage message =
+ NanoAppMessage.createMessageToNanoApp(
+ PresenceManager.NANOAPP_ID,
+ PresenceManager.NANOAPP_MESSAGE_TYPE_FILTER_RESULT,
+ new byte[] {1, 2, 3});
+ mChreCommunication.onMessageFromNanoApp(mClient, message);
+ verify(mChreCallback).onMessageFromNanoApp(eq(message));
+ }
+
+ @Test
+ public void testOnHubReset() {
+ mChreCommunication.onHubReset(mClient);
+ verify(mChreCallback).onHubReset();
+ }
+
+ @Test
+ public void testOnNanoAppLoaded() {
+ mChreCommunication.onNanoAppLoaded(mClient, PresenceManager.NANOAPP_ID);
+ verify(mChreCallback).onNanoAppRestart(eq(PresenceManager.NANOAPP_ID));
+ }
+
+ private static class InlineExecutor implements Executor {
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
new file mode 100644
index 0000000..f9676e3
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.presence;
+
+import static com.android.server.nearby.presence.PresenceManager.NANOAPP_ID;
+import static com.android.server.nearby.presence.PresenceManager.NANOAPP_MESSAGE_TYPE_FILTER_RESULT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.hardware.location.NanoAppMessage;
+
+import androidx.test.filters.SdkSuppress;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+
+import service.proto.Blefilter;
+
+public class PresenceManagerTest {
+ @Mock private Context mContext;
+ @Mock private PresenceManager.PresenceCallback mPresenceCallback;
+ @Mock private ChreCommunication mChreCommunication;
+
+ @Captor ArgumentCaptor<ChreCommunication.ContextHubCommsCallback> mChreCallbackCaptor;
+ @Captor ArgumentCaptor<NanoAppMessage> mNanoAppMessageCaptor;
+
+ private PresenceManager mPresenceManager;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mPresenceManager = new PresenceManager(mContext, mPresenceCallback);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void testInit() {
+ mPresenceManager.initiate(mChreCommunication);
+ verify(mChreCommunication).start(any(), eq(Collections.singleton(NANOAPP_ID)));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void testOnFilterResults() {
+ Blefilter.BleFilterResults results = Blefilter.BleFilterResults.newBuilder().build();
+ NanoAppMessage chre_message =
+ NanoAppMessage.createMessageToNanoApp(
+ NANOAPP_ID, NANOAPP_MESSAGE_TYPE_FILTER_RESULT, results.toByteArray());
+ mPresenceManager.initiate(mChreCommunication);
+ verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
+ mChreCallbackCaptor.getValue().onMessageFromNanoApp(chre_message);
+ verify(mPresenceCallback).onFilterResults(eq(results));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void testUpdateFiltersBeforeChreStarted() {
+ Blefilter.BleFilters filters = Blefilter.BleFilters.newBuilder().build();
+ mPresenceManager.updateFilters(filters);
+ verify(mChreCommunication, never()).sendMessageToNanoApp(any());
+ mPresenceManager.initiate(mChreCommunication);
+ verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
+ mChreCallbackCaptor.getValue().started(true);
+ verify(mChreCommunication, times(1)).sendMessageToNanoApp(mNanoAppMessageCaptor.capture());
+ assertThat(mNanoAppMessageCaptor.getValue().getMessageBody())
+ .isEqualTo(filters.toByteArray());
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void testUpdateFiltersAfterChreStarted() {
+ mPresenceManager.initiate(mChreCommunication);
+ verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
+ mChreCallbackCaptor.getValue().started(true);
+ Blefilter.BleFilters filters = Blefilter.BleFilters.newBuilder().build();
+ mPresenceManager.updateFilters(filters);
+ verify(mChreCommunication, times(1)).sendMessageToNanoApp(mNanoAppMessageCaptor.capture());
+ assertThat(mNanoAppMessageCaptor.getValue().getMessageBody())
+ .isEqualTo(filters.toByteArray());
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java
index 4c78885..8e97443 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java
@@ -32,6 +32,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.server.nearby.injector.ContextHubManagerAdapter;
import com.android.server.nearby.injector.Injector;
import org.junit.Before;
@@ -81,6 +82,11 @@
public BluetoothAdapter getBluetoothAdapter() {
return mBluetoothAdapter;
}
+
+ @Override
+ public ContextHubManagerAdapter getContextHubManagerAdapter() {
+ return null;
+ }
}
private ScanResult createScanResult() {