Change the DiscoveryProviderManager to use the Multiplexer logic
Fix: 263893748
Test: e2e with test app
Ignore-AOSP-First: nearby_not_in_aosp_yet
Change-Id: I8e8d540951ce5afba03b7bb66269b519d19f2f35
diff --git a/nearby/service/java/com/android/server/nearby/NearbyService.java b/nearby/service/java/com/android/server/nearby/NearbyService.java
index 8d35e08..2ac2b00 100644
--- a/nearby/service/java/com/android/server/nearby/NearbyService.java
+++ b/nearby/service/java/com/android/server/nearby/NearbyService.java
@@ -45,7 +45,9 @@
import com.android.server.nearby.fastpair.FastPairManager;
import com.android.server.nearby.injector.Injector;
import com.android.server.nearby.managers.BroadcastProviderManager;
+import com.android.server.nearby.managers.DiscoveryManager;
import com.android.server.nearby.managers.DiscoveryProviderManager;
+import com.android.server.nearby.managers.DiscoveryProviderManagerLegacy;
import com.android.server.nearby.presence.PresenceManager;
import com.android.server.nearby.provider.FastPairDataProvider;
import com.android.server.nearby.util.identity.CallerIdentity;
@@ -80,18 +82,21 @@
}
}
};
- private final DiscoveryProviderManager mProviderManager;
+ private final DiscoveryManager mDiscoveryProviderManager;
private final BroadcastProviderManager mBroadcastProviderManager;
public NearbyService(Context context) {
mContext = context;
mInjector = new SystemInjector(context);
- mProviderManager = new DiscoveryProviderManager(context, mInjector);
mBroadcastProviderManager = new BroadcastProviderManager(context, mInjector);
final LocatorContextWrapper lcw = new LocatorContextWrapper(context, null);
mFastPairManager = new FastPairManager(lcw);
mPresenceManager = new PresenceManager(lcw);
mNearbyConfiguration = new NearbyConfiguration();
+ mDiscoveryProviderManager =
+ mNearbyConfiguration.refactorDiscoveryManager()
+ ? new DiscoveryProviderManager(context, mInjector)
+ : new DiscoveryProviderManagerLegacy(context, mInjector);
}
@VisibleForTesting
@@ -108,7 +113,7 @@
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
DiscoveryPermissions.enforceDiscoveryPermission(mContext, identity);
- return mProviderManager.registerScanListener(scanRequest, listener, identity);
+ return mDiscoveryProviderManager.registerScanListener(scanRequest, listener, identity);
}
@Override
@@ -119,7 +124,7 @@
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
DiscoveryPermissions.enforceDiscoveryPermission(mContext, identity);
- mProviderManager.unregisterScanListener(listener);
+ mDiscoveryProviderManager.unregisterScanListener(listener);
}
@Override
@@ -147,7 +152,7 @@
@Override
public void queryOffloadCapability(IOffloadCallback callback) {
- mProviderManager.queryOffloadCapability(callback);
+ mDiscoveryProviderManager.queryOffloadCapability(callback);
}
/**
@@ -174,7 +179,7 @@
// Initialize ContextManager for CHRE scan.
((SystemInjector) mInjector).initializeContextHubManager();
}
- mProviderManager.init();
+ mDiscoveryProviderManager.init();
mContext.registerReceiver(
mBluetoothReceiver,
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
diff --git a/nearby/service/java/com/android/server/nearby/managers/DiscoveryManager.java b/nearby/service/java/com/android/server/nearby/managers/DiscoveryManager.java
new file mode 100644
index 0000000..c9b9a43
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/managers/DiscoveryManager.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 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.managers;
+
+import android.nearby.IScanListener;
+import android.nearby.NearbyManager;
+import android.nearby.ScanRequest;
+import android.nearby.aidl.IOffloadCallback;
+
+import com.android.server.nearby.util.identity.CallerIdentity;
+
+/**
+ * Interface added for flagging DiscoveryProviderManager refactor. After the
+ * nearby_refactor_discovery_manager flag is fully rolled out, this can be deleted.
+ */
+public interface DiscoveryManager {
+
+ /**
+ * Registers the listener in the manager and starts scan according to the requested scan mode.
+ */
+ @NearbyManager.ScanStatus
+ int registerScanListener(ScanRequest scanRequest, IScanListener listener,
+ CallerIdentity callerIdentity);
+
+ /**
+ * Unregisters the listener in the manager and adjusts the scan mode if necessary afterwards.
+ */
+ void unregisterScanListener(IScanListener listener);
+
+ /** Query offload capability in a device. */
+ void queryOffloadCapability(IOffloadCallback callback);
+
+ /** Called after boot completed. */
+ void init();
+}
diff --git a/nearby/service/java/com/android/server/nearby/managers/DiscoveryProviderManager.java b/nearby/service/java/com/android/server/nearby/managers/DiscoveryProviderManager.java
index 29b18f3..4cda93a 100644
--- a/nearby/service/java/com/android/server/nearby/managers/DiscoveryProviderManager.java
+++ b/nearby/service/java/com/android/server/nearby/managers/DiscoveryProviderManager.java
@@ -21,320 +21,74 @@
import static com.android.server.nearby.NearbyService.TAG;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.content.Context;
import android.nearby.DataElement;
import android.nearby.IScanListener;
import android.nearby.NearbyDeviceParcelable;
import android.nearby.NearbyManager;
import android.nearby.PresenceScanFilter;
-import android.nearby.ScanCallback;
import android.nearby.ScanFilter;
import android.nearby.ScanRequest;
import android.nearby.aidl.IOffloadCallback;
-import android.os.IBinder;
-import android.os.RemoteException;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.nearby.injector.Injector;
-import com.android.server.nearby.metrics.NearbyMetrics;
-import com.android.server.nearby.presence.PresenceDiscoveryResult;
+import com.android.server.nearby.managers.registration.DiscoveryRegistration;
import com.android.server.nearby.provider.AbstractDiscoveryProvider;
import com.android.server.nearby.provider.BleDiscoveryProvider;
import com.android.server.nearby.provider.ChreCommunication;
import com.android.server.nearby.provider.ChreDiscoveryProvider;
-import com.android.server.nearby.provider.PrivacyFilter;
import com.android.server.nearby.util.identity.CallerIdentity;
-import com.android.server.nearby.util.permissions.DiscoveryPermissions;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
-import java.util.stream.Collectors;
+
+import javax.annotation.concurrent.GuardedBy;
/** Manages all aspects of discovery providers. */
-public class DiscoveryProviderManager implements AbstractDiscoveryProvider.Listener {
+public class DiscoveryProviderManager extends
+ ListenerMultiplexer<IScanListener, DiscoveryRegistration, MergedDiscoveryRequest> implements
+ AbstractDiscoveryProvider.Listener,
+ DiscoveryManager {
protected final Object mLock = new Object();
- private final Context mContext;
- private final BleDiscoveryProvider mBleDiscoveryProvider;
@VisibleForTesting
@Nullable
final ChreDiscoveryProvider mChreDiscoveryProvider;
- private @ScanRequest.ScanMode int mScanMode;
+ private final Context mContext;
+ private final BleDiscoveryProvider mBleDiscoveryProvider;
private final Injector mInjector;
-
- @GuardedBy("mLock")
- private Map<IBinder, ScanListenerRecord> mScanTypeScanListenerRecordMap;
-
- @Override
- public void onNearbyDeviceDiscovered(NearbyDeviceParcelable nearbyDevice) {
- synchronized (mLock) {
- AppOpsManager appOpsManager = Objects.requireNonNull(mInjector.getAppOpsManager());
- for (IBinder listenerBinder : mScanTypeScanListenerRecordMap.keySet()) {
- ScanListenerRecord record = mScanTypeScanListenerRecordMap.get(listenerBinder);
- if (record == null) {
- Log.w(TAG, "DiscoveryProviderManager cannot find the scan record.");
- continue;
- }
- CallerIdentity callerIdentity = record.getCallerIdentity();
- if (!DiscoveryPermissions.noteDiscoveryResultDelivery(
- appOpsManager, callerIdentity)) {
- Log.w(TAG, "[DiscoveryProviderManager] scan permission revoked "
- + "- not forwarding results");
- try {
- record.getScanListener().onError(ScanCallback.ERROR_PERMISSION_DENIED);
- } catch (RemoteException e) {
- Log.w(TAG, "DiscoveryProviderManager failed to report error.", e);
- }
- return;
- }
-
- if (nearbyDevice.getScanType() == SCAN_TYPE_NEARBY_PRESENCE) {
- List<ScanFilter> presenceFilters =
- record.getScanRequest().getScanFilters().stream()
- .filter(
- scanFilter ->
- scanFilter.getType()
- == SCAN_TYPE_NEARBY_PRESENCE)
- .collect(Collectors.toList());
- if (!presenceFilterMatches(nearbyDevice, presenceFilters)) {
- Log.d(TAG, "presence filter does not match for "
- + "the scanned Presence Device");
- continue;
- }
- }
- try {
- record.getScanListener()
- .onDiscovered(
- PrivacyFilter.filter(
- record.getScanRequest().getScanType(), nearbyDevice));
- NearbyMetrics.logScanDeviceDiscovered(
- record.hashCode(), record.getScanRequest(), nearbyDevice);
- } catch (RemoteException e) {
- Log.w(TAG, "DiscoveryProviderManager failed to report onDiscovered.", e);
- }
- }
- }
- }
-
- @Override
- public void onError(int errorCode) {
- synchronized (mLock) {
- AppOpsManager appOpsManager = Objects.requireNonNull(mInjector.getAppOpsManager());
- for (IBinder listenerBinder : mScanTypeScanListenerRecordMap.keySet()) {
- ScanListenerRecord record = mScanTypeScanListenerRecordMap.get(listenerBinder);
- if (record == null) {
- Log.w(TAG, "DiscoveryProviderManager cannot find the scan record.");
- continue;
- }
- CallerIdentity callerIdentity = record.getCallerIdentity();
- if (!DiscoveryPermissions.noteDiscoveryResultDelivery(
- appOpsManager, callerIdentity)) {
- Log.w(TAG, "[DiscoveryProviderManager] scan permission revoked "
- + "- not forwarding results");
- try {
- record.getScanListener().onError(ScanCallback.ERROR_PERMISSION_DENIED);
- } catch (RemoteException e) {
- Log.w(TAG, "DiscoveryProviderManager failed to report error.", e);
- }
- return;
- }
-
- try {
- record.getScanListener().onError(errorCode);
- } catch (RemoteException e) {
- Log.w(TAG, "DiscoveryProviderManager failed to report onError.", e);
- }
- }
- }
- }
+ private final Executor mExecutor;
public DiscoveryProviderManager(Context context, Injector injector) {
+ Log.v(TAG, "DiscoveryProviderManager: ");
mContext = context;
mBleDiscoveryProvider = new BleDiscoveryProvider(mContext, injector);
- Executor executor = Executors.newSingleThreadExecutor();
- mChreDiscoveryProvider =
- new ChreDiscoveryProvider(
- mContext, new ChreCommunication(injector, mContext, executor), executor);
- mScanTypeScanListenerRecordMap = new HashMap<>();
+ mExecutor = Executors.newSingleThreadExecutor();
+ mChreDiscoveryProvider = new ChreDiscoveryProvider(mContext,
+ new ChreCommunication(injector, mContext, mExecutor), mExecutor);
mInjector = injector;
}
@VisibleForTesting
- DiscoveryProviderManager(Context context, Injector injector,
+ DiscoveryProviderManager(Context context, Executor executor, Injector injector,
BleDiscoveryProvider bleDiscoveryProvider,
- ChreDiscoveryProvider chreDiscoveryProvider,
- Map<IBinder, ScanListenerRecord> scanTypeScanListenerRecordMap) {
+ ChreDiscoveryProvider chreDiscoveryProvider) {
mContext = context;
+ mExecutor = executor;
mInjector = injector;
mBleDiscoveryProvider = bleDiscoveryProvider;
mChreDiscoveryProvider = chreDiscoveryProvider;
- mScanTypeScanListenerRecordMap = scanTypeScanListenerRecordMap;
}
- /** Called after boot completed. */
- public void init() {
- if (mInjector.getContextHubManager() != null) {
- mChreDiscoveryProvider.init();
- }
- mChreDiscoveryProvider.getController().setListener(this);
- }
-
- /**
- * Registers the listener in the manager and starts scan according to the requested scan mode.
- */
- @NearbyManager.ScanStatus
- public int registerScanListener(ScanRequest scanRequest, IScanListener listener,
- CallerIdentity callerIdentity) {
- synchronized (mLock) {
- ScanListenerDeathRecipient deathRecipient = (listener != null)
- ? new ScanListenerDeathRecipient(listener) : null;
- IBinder listenerBinder = listener.asBinder();
- if (listenerBinder != null && deathRecipient != null) {
- try {
- listenerBinder.linkToDeath(deathRecipient, 0);
- } catch (RemoteException e) {
- throw new IllegalArgumentException("Can't link to scan listener's death");
- }
- }
- if (mScanTypeScanListenerRecordMap.containsKey(listener.asBinder())) {
- ScanRequest savedScanRequest =
- mScanTypeScanListenerRecordMap.get(listenerBinder).getScanRequest();
- if (scanRequest.equals(savedScanRequest)) {
- Log.d(TAG, "Already registered the scanRequest: " + scanRequest);
- return NearbyManager.ScanStatus.SUCCESS;
- }
- }
- ScanListenerRecord scanListenerRecord =
- new ScanListenerRecord(scanRequest, listener, callerIdentity, deathRecipient);
-
- Boolean started = startProviders(scanRequest);
- if (started == null) {
- return NearbyManager.ScanStatus.UNKNOWN;
- }
- if (!started) {
- return NearbyManager.ScanStatus.ERROR;
- }
- mScanTypeScanListenerRecordMap.put(listenerBinder, scanListenerRecord);
- NearbyMetrics.logScanStarted(scanListenerRecord.hashCode(), scanRequest);
- if (mScanMode < scanRequest.getScanMode()) {
- mScanMode = scanRequest.getScanMode();
- invalidateProviderScanMode();
- }
- return NearbyManager.ScanStatus.SUCCESS;
- }
- }
-
- /**
- * 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(listenerBinder)) {
- Log.w(
- TAG,
- "Cannot unregister the scanRequest because the request is never "
- + "registered.");
- return;
- }
-
- ScanListenerRecord removedRecord =
- mScanTypeScanListenerRecordMap.remove(listenerBinder);
- ScanListenerDeathRecipient deathRecipient = removedRecord.getDeathRecipient();
- if (listenerBinder != null && deathRecipient != null) {
- listenerBinder.unlinkToDeath(removedRecord.getDeathRecipient(), 0);
- }
- Log.v(TAG, "DiscoveryProviderManager unregistered scan listener.");
- NearbyMetrics.logScanStopped(removedRecord.hashCode(), removedRecord.getScanRequest());
- if (mScanTypeScanListenerRecordMap.isEmpty()) {
- Log.v(TAG, "DiscoveryProviderManager stops provider because there is no "
- + "scan listener registered.");
- stopProviders();
- return;
- }
-
- // TODO(b/221082271): updates the scan with reduced filters.
-
- // Removes current highest scan mode requested and sets the next highest scan mode.
- if (removedRecord.getScanRequest().getScanMode() == mScanMode) {
- Log.v(TAG, "DiscoveryProviderManager starts to find the new highest scan mode "
- + "because the highest scan mode listener was unregistered.");
- @ScanRequest.ScanMode int highestScanModeRequested = ScanRequest.SCAN_MODE_NO_POWER;
- // find the next highest scan mode;
- for (ScanListenerRecord record : mScanTypeScanListenerRecordMap.values()) {
- @ScanRequest.ScanMode int scanMode = record.getScanRequest().getScanMode();
- if (scanMode > highestScanModeRequested) {
- highestScanModeRequested = scanMode;
- }
- }
- if (mScanMode != highestScanModeRequested) {
- mScanMode = highestScanModeRequested;
- invalidateProviderScanMode();
- }
- }
- }
- }
-
- /**
- * Query offload capability in a device.
- */
- public void queryOffloadCapability(IOffloadCallback callback) {
- mChreDiscoveryProvider.queryOffloadCapability(callback);
- }
-
- /**
- * @return {@code null} when all providers are initializing
- * {@code false} when fail to start all the providers
- * {@code true} when any one of the provider starts successfully
- */
- @VisibleForTesting
- @Nullable
- Boolean startProviders(ScanRequest scanRequest) {
- if (!scanRequest.isBleEnabled()) {
- Log.w(TAG, "failed to start any provider because client disabled BLE");
- return false;
- }
- List<ScanFilter> scanFilters = getPresenceScanFilters();
- boolean chreOnly = isChreOnly(scanFilters);
- Boolean chreAvailable = mChreDiscoveryProvider.available();
- if (chreAvailable == null) {
- if (chreOnly) {
- Log.w(TAG, "client wants CHRE only and Nearby service is still querying CHRE"
- + " status");
- return null;
- }
- startBleProvider(scanFilters);
- return true;
- }
-
- if (!chreAvailable) {
- if (chreOnly) {
- Log.w(TAG, "failed to start any provider because client wants CHRE only and CHRE"
- + " is not available");
- return false;
- }
- startBleProvider(scanFilters);
- return true;
- }
-
- if (scanRequest.getScanType() == SCAN_TYPE_NEARBY_PRESENCE) {
- startChreProvider(scanFilters);
- return true;
- }
-
- startBleProvider(scanFilters);
- return true;
- }
-
- private static boolean isChreOnly(List<ScanFilter> scanFilters) {
+ private static boolean isChreOnly(Set<ScanFilter> scanFilters) {
for (ScanFilter scanFilter : scanFilters) {
List<DataElement> dataElements =
((PresenceScanFilter) scanFilter).getExtendedProperties();
@@ -355,41 +109,160 @@
return false;
}
- private void startBleProvider(List<ScanFilter> scanFilters) {
+ @Override
+ public void onNearbyDeviceDiscovered(NearbyDeviceParcelable nearbyDevice) {
+ synchronized (mMultiplexerLock) {
+ Log.d(TAG, "Found device" + nearbyDevice);
+ deliverToListeners(registration -> {
+ try {
+ return registration.onNearbyDeviceDiscovered(nearbyDevice);
+ } catch (Exception e) {
+ Log.w(TAG, "DiscoveryProviderManager failed to report callback.", e);
+ return null;
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onError(int errorCode) {
+ synchronized (mMultiplexerLock) {
+ Log.e(TAG, "Error found during scanning.");
+ deliverToListeners(registration -> {
+ try {
+ return registration.reportError(errorCode);
+ } catch (Exception e) {
+ Log.w(TAG, "DiscoveryProviderManager failed to report error.", e);
+ return null;
+ }
+ });
+ }
+ }
+
+ /** Called after boot completed. */
+ public void init() {
+ if (mInjector.getContextHubManager() != null) {
+ mChreDiscoveryProvider.init();
+ }
+ mChreDiscoveryProvider.getController().setListener(this);
+ }
+
+ /**
+ * Registers the listener in the manager and starts scan according to the requested scan mode.
+ */
+ @NearbyManager.ScanStatus
+ public int registerScanListener(ScanRequest scanRequest, IScanListener listener,
+ CallerIdentity callerIdentity) {
+ DiscoveryRegistration registration = new DiscoveryRegistration(this, scanRequest, listener,
+ mExecutor, callerIdentity, mMultiplexerLock, mInjector.getAppOpsManager());
+ synchronized (mMultiplexerLock) {
+ putRegistration(listener.asBinder(), registration);
+ return NearbyManager.ScanStatus.SUCCESS;
+ }
+ }
+
+ @Override
+ public void onRegister() {
+ Log.v(TAG, "Registering the DiscoveryProviderManager.");
+ startProviders();
+ }
+
+ @Override
+ public void onUnregister() {
+ Log.v(TAG, "Unregistering the DiscoveryProviderManager.");
+ stopProviders();
+ }
+
+ /**
+ * Unregisters the listener in the manager and adjusts the scan mode if necessary afterwards.
+ */
+ public void unregisterScanListener(IScanListener listener) {
+ Log.v(TAG, "Unregister scan listener");
+ synchronized (mMultiplexerLock) {
+ removeRegistration(listener.asBinder());
+ }
+ // TODO(b/221082271): updates the scan with reduced filters.
+ }
+
+ /**
+ * Query offload capability in a device.
+ */
+ public void queryOffloadCapability(IOffloadCallback callback) {
+ mChreDiscoveryProvider.queryOffloadCapability(callback);
+ }
+
+ /**
+ * @return {@code null} when all providers are initializing
+ * {@code false} when fail to start all the providers
+ * {@code true} when any one of the provider starts successfully
+ */
+ @VisibleForTesting
+ @Nullable
+ Boolean startProviders() {
+ synchronized (mMultiplexerLock) {
+ if (!mMerged.getMediums().contains(MergedDiscoveryRequest.Medium.BLE)) {
+ Log.w(TAG, "failed to start any provider because client disabled BLE");
+ return false;
+ }
+ Set<ScanFilter> scanFilters = mMerged.getScanFilters();
+ boolean chreOnly = isChreOnly(scanFilters);
+ Boolean chreAvailable = mChreDiscoveryProvider.available();
+ Log.v(TAG, "startProviders: chreOnly " + chreOnly + " chreAvailable " + chreAvailable);
+ if (chreAvailable == null) {
+ if (chreOnly) {
+ Log.w(TAG, "client wants CHRE only and Nearby service is still querying CHRE"
+ + " status");
+ return null;
+ }
+ startBleProvider(scanFilters);
+ return true;
+ }
+
+ if (!chreAvailable) {
+ if (chreOnly) {
+ Log.w(TAG,
+ "failed to start any provider because client wants CHRE only and CHRE"
+ + " is not available");
+ return false;
+ }
+ startBleProvider(scanFilters);
+ return true;
+ }
+
+ if (mMerged.getScanTypes().contains(SCAN_TYPE_NEARBY_PRESENCE)) {
+ startChreProvider(scanFilters);
+ Log.d(TAG, "startProviders: heeey");
+ return true;
+ }
+
+ Log.d(TAG, "startProviders: geagewage");
+
+ startBleProvider(scanFilters);
+ return true;
+ }
+ }
+
+ @GuardedBy("mMultiplexerLock")
+ private void startBleProvider(Set<ScanFilter> scanFilters) {
if (!mBleDiscoveryProvider.getController().isStarted()) {
Log.d(TAG, "DiscoveryProviderManager starts Ble scanning.");
mBleDiscoveryProvider.getController().setListener(this);
- mBleDiscoveryProvider.getController().setProviderScanMode(mScanMode);
- mBleDiscoveryProvider.getController().setProviderScanFilters(scanFilters);
+ mBleDiscoveryProvider.getController().setProviderScanMode(mMerged.getScanMode());
+ mBleDiscoveryProvider.getController().setProviderScanFilters(
+ new ArrayList<>(scanFilters));
mBleDiscoveryProvider.getController().start();
}
}
@VisibleForTesting
- void startChreProvider(List<ScanFilter> scanFilters) {
- Log.d(TAG, "DiscoveryProviderManager starts CHRE scanning.");
- mChreDiscoveryProvider.getController().setProviderScanFilters(scanFilters);
- mChreDiscoveryProvider.getController().setProviderScanMode(mScanMode);
+ @GuardedBy("mMultiplexerLock")
+ void startChreProvider(Collection<ScanFilter> scanFilters) {
+ Log.d(TAG, "DiscoveryProviderManager starts CHRE scanning. " + mMerged);
+ mChreDiscoveryProvider.getController().setProviderScanFilters(new ArrayList<>(scanFilters));
+ mChreDiscoveryProvider.getController().setProviderScanMode(mMerged.getScanMode());
mChreDiscoveryProvider.getController().start();
}
- private List<ScanFilter> getPresenceScanFilters() {
- synchronized (mLock) {
- List<ScanFilter> scanFilters = new ArrayList();
- for (IBinder listenerBinder : mScanTypeScanListenerRecordMap.keySet()) {
- ScanListenerRecord record = mScanTypeScanListenerRecordMap.get(listenerBinder);
- List<ScanFilter> presenceFilters =
- record.getScanRequest().getScanFilters().stream()
- .filter(
- scanFilter ->
- scanFilter.getType() == SCAN_TYPE_NEARBY_PRESENCE)
- .collect(Collectors.toList());
- scanFilters.addAll(presenceFilters);
- }
- return scanFilters;
- }
- }
-
private void stopProviders() {
stopBleProvider();
stopChreProvider();
@@ -407,96 +280,40 @@
@VisibleForTesting
void invalidateProviderScanMode() {
if (mBleDiscoveryProvider.getController().isStarted()) {
- mBleDiscoveryProvider.getController().setProviderScanMode(mScanMode);
+ synchronized (mMultiplexerLock) {
+ mBleDiscoveryProvider.getController().setProviderScanMode(mMerged.getScanMode());
+ }
} else {
- Log.d(
- TAG,
- "Skip invalidating BleDiscoveryProvider scan mode because the provider not "
- + "started.");
+ Log.d(TAG, "Skip invalidating BleDiscoveryProvider scan mode because the provider not "
+ + "started.");
}
}
- @VisibleForTesting
- static boolean presenceFilterMatches(
- NearbyDeviceParcelable device, List<ScanFilter> scanFilters) {
- if (scanFilters.isEmpty()) {
- return true;
- }
- PresenceDiscoveryResult discoveryResult = PresenceDiscoveryResult.fromDevice(device);
- for (ScanFilter scanFilter : scanFilters) {
- PresenceScanFilter presenceScanFilter = (PresenceScanFilter) scanFilter;
- if (discoveryResult.matches(presenceScanFilter)) {
- return true;
+ @Override
+ public MergedDiscoveryRequest mergeRegistrations(
+ @NonNull Collection<DiscoveryRegistration> registrations) {
+ MergedDiscoveryRequest.Builder builder = new MergedDiscoveryRequest.Builder();
+ int scanMode = ScanRequest.SCAN_MODE_NO_POWER;
+ for (DiscoveryRegistration registration : registrations) {
+ builder.addActions(registration.getActions());
+ builder.addScanFilters(registration.getPresenceScanFilters());
+ Log.d(TAG,
+ "mergeRegistrations: type is " + registration.getScanRequest().getScanType());
+ builder.addScanType(registration.getScanRequest().getScanType());
+ if (registration.getScanRequest().isBleEnabled()) {
+ builder.addMedium(MergedDiscoveryRequest.Medium.BLE);
+ }
+ int requestScanMode = registration.getScanRequest().getScanMode();
+ if (scanMode < requestScanMode) {
+ scanMode = requestScanMode;
}
}
- return false;
+ builder.setScanMode(scanMode);
+ return builder.build();
}
- /**
- * Class to make listener unregister after the binder is dead.
- */
- public class ScanListenerDeathRecipient implements IBinder.DeathRecipient {
- public IScanListener listener;
-
- ScanListenerDeathRecipient(IScanListener listener) {
- this.listener = listener;
- }
-
- @Override
- public void binderDied() {
- Log.d(TAG, "Binder is dead - unregistering scan listener");
- unregisterScanListener(listener);
- }
- }
-
- @VisibleForTesting
- static class ScanListenerRecord {
-
- private final ScanRequest mScanRequest;
-
- private final IScanListener mScanListener;
-
- private final CallerIdentity mCallerIdentity;
-
- private final ScanListenerDeathRecipient mDeathRecipient;
-
- ScanListenerRecord(ScanRequest scanRequest, IScanListener iScanListener,
- CallerIdentity callerIdentity, ScanListenerDeathRecipient deathRecipient) {
- mScanListener = iScanListener;
- mScanRequest = scanRequest;
- mCallerIdentity = callerIdentity;
- mDeathRecipient = deathRecipient;
- }
-
- IScanListener getScanListener() {
- return mScanListener;
- }
-
- ScanRequest getScanRequest() {
- return mScanRequest;
- }
-
- CallerIdentity getCallerIdentity() {
- return mCallerIdentity;
- }
-
- ScanListenerDeathRecipient getDeathRecipient() {
- return mDeathRecipient;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other instanceof ScanListenerRecord) {
- ScanListenerRecord otherScanListenerRecord = (ScanListenerRecord) other;
- return Objects.equals(mScanRequest, otherScanListenerRecord.mScanRequest)
- && Objects.equals(mScanListener, otherScanListenerRecord.mScanListener);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mScanListener, mScanRequest);
- }
+ @Override
+ public void onMergedRegistrationsUpdated() {
+ invalidateProviderScanMode();
}
}
diff --git a/nearby/service/java/com/android/server/nearby/managers/DiscoveryProviderManagerLegacy.java b/nearby/service/java/com/android/server/nearby/managers/DiscoveryProviderManagerLegacy.java
new file mode 100644
index 0000000..e68d22a
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/managers/DiscoveryProviderManagerLegacy.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2023 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.managers;
+
+import static android.nearby.ScanRequest.SCAN_TYPE_NEARBY_PRESENCE;
+
+import static com.android.server.nearby.NearbyService.TAG;
+
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.nearby.DataElement;
+import android.nearby.IScanListener;
+import android.nearby.NearbyDeviceParcelable;
+import android.nearby.NearbyManager;
+import android.nearby.PresenceScanFilter;
+import android.nearby.ScanCallback;
+import android.nearby.ScanFilter;
+import android.nearby.ScanRequest;
+import android.nearby.aidl.IOffloadCallback;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.nearby.injector.Injector;
+import com.android.server.nearby.metrics.NearbyMetrics;
+import com.android.server.nearby.presence.PresenceDiscoveryResult;
+import com.android.server.nearby.provider.AbstractDiscoveryProvider;
+import com.android.server.nearby.provider.BleDiscoveryProvider;
+import com.android.server.nearby.provider.ChreCommunication;
+import com.android.server.nearby.provider.ChreDiscoveryProvider;
+import com.android.server.nearby.provider.PrivacyFilter;
+import com.android.server.nearby.util.identity.CallerIdentity;
+import com.android.server.nearby.util.permissions.DiscoveryPermissions;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+
+/** Manages all aspects of discovery providers. */
+public class DiscoveryProviderManagerLegacy implements AbstractDiscoveryProvider.Listener,
+ DiscoveryManager {
+
+ protected final Object mLock = new Object();
+ @VisibleForTesting
+ @Nullable
+ final ChreDiscoveryProvider mChreDiscoveryProvider;
+ private final Context mContext;
+ private final BleDiscoveryProvider mBleDiscoveryProvider;
+ private final Injector mInjector;
+ @ScanRequest.ScanMode
+ private int mScanMode;
+ @GuardedBy("mLock")
+ private final Map<IBinder, ScanListenerRecord> mScanTypeScanListenerRecordMap;
+
+ public DiscoveryProviderManagerLegacy(Context context, Injector injector) {
+ mContext = context;
+ mBleDiscoveryProvider = new BleDiscoveryProvider(mContext, injector);
+ Executor executor = Executors.newSingleThreadExecutor();
+ mChreDiscoveryProvider =
+ new ChreDiscoveryProvider(
+ mContext, new ChreCommunication(injector, mContext, executor), executor);
+ mScanTypeScanListenerRecordMap = new HashMap<>();
+ mInjector = injector;
+ Log.v(TAG, "DiscoveryProviderManagerLegacy: ");
+ }
+
+ @VisibleForTesting
+ DiscoveryProviderManagerLegacy(Context context, Injector injector,
+ BleDiscoveryProvider bleDiscoveryProvider,
+ ChreDiscoveryProvider chreDiscoveryProvider,
+ Map<IBinder, ScanListenerRecord> scanTypeScanListenerRecordMap) {
+ mContext = context;
+ mInjector = injector;
+ mBleDiscoveryProvider = bleDiscoveryProvider;
+ mChreDiscoveryProvider = chreDiscoveryProvider;
+ mScanTypeScanListenerRecordMap = scanTypeScanListenerRecordMap;
+ }
+
+ private static boolean isChreOnly(List<ScanFilter> scanFilters) {
+ for (ScanFilter scanFilter : scanFilters) {
+ List<DataElement> dataElements =
+ ((PresenceScanFilter) scanFilter).getExtendedProperties();
+ for (DataElement dataElement : dataElements) {
+ if (dataElement.getKey() != DataElement.DataType.SCAN_MODE) {
+ continue;
+ }
+ byte[] scanModeValue = dataElement.getValue();
+ if (scanModeValue == null || scanModeValue.length == 0) {
+ break;
+ }
+ if (Byte.toUnsignedInt(scanModeValue[0]) == ScanRequest.SCAN_MODE_CHRE_ONLY) {
+ return true;
+ }
+ }
+
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ static boolean presenceFilterMatches(
+ NearbyDeviceParcelable device, List<ScanFilter> scanFilters) {
+ if (scanFilters.isEmpty()) {
+ return true;
+ }
+ PresenceDiscoveryResult discoveryResult = PresenceDiscoveryResult.fromDevice(device);
+ for (ScanFilter scanFilter : scanFilters) {
+ PresenceScanFilter presenceScanFilter = (PresenceScanFilter) scanFilter;
+ if (discoveryResult.matches(presenceScanFilter)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onNearbyDeviceDiscovered(NearbyDeviceParcelable nearbyDevice) {
+ synchronized (mLock) {
+ AppOpsManager appOpsManager = Objects.requireNonNull(mInjector.getAppOpsManager());
+ for (IBinder listenerBinder : mScanTypeScanListenerRecordMap.keySet()) {
+ ScanListenerRecord record = mScanTypeScanListenerRecordMap.get(listenerBinder);
+ if (record == null) {
+ Log.w(TAG, "DiscoveryProviderManager cannot find the scan record.");
+ continue;
+ }
+ CallerIdentity callerIdentity = record.getCallerIdentity();
+ if (!DiscoveryPermissions.noteDiscoveryResultDelivery(
+ appOpsManager, callerIdentity)) {
+ Log.w(TAG, "[DiscoveryProviderManager] scan permission revoked "
+ + "- not forwarding results");
+ try {
+ record.getScanListener().onError(ScanCallback.ERROR_PERMISSION_DENIED);
+ } catch (RemoteException e) {
+ Log.w(TAG, "DiscoveryProviderManager failed to report error.", e);
+ }
+ return;
+ }
+
+ if (nearbyDevice.getScanType() == SCAN_TYPE_NEARBY_PRESENCE) {
+ List<ScanFilter> presenceFilters =
+ record.getScanRequest().getScanFilters().stream()
+ .filter(
+ scanFilter ->
+ scanFilter.getType()
+ == SCAN_TYPE_NEARBY_PRESENCE)
+ .collect(Collectors.toList());
+ if (!presenceFilterMatches(nearbyDevice, presenceFilters)) {
+ Log.d(TAG, "presence filter does not match for "
+ + "the scanned Presence Device");
+ continue;
+ }
+ }
+ try {
+ record.getScanListener()
+ .onDiscovered(
+ PrivacyFilter.filter(
+ record.getScanRequest().getScanType(), nearbyDevice));
+ NearbyMetrics.logScanDeviceDiscovered(
+ record.hashCode(), record.getScanRequest(), nearbyDevice);
+ } catch (RemoteException e) {
+ Log.w(TAG, "DiscoveryProviderManager failed to report onDiscovered.", e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onError(int errorCode) {
+ synchronized (mLock) {
+ AppOpsManager appOpsManager = Objects.requireNonNull(mInjector.getAppOpsManager());
+ for (IBinder listenerBinder : mScanTypeScanListenerRecordMap.keySet()) {
+ ScanListenerRecord record = mScanTypeScanListenerRecordMap.get(listenerBinder);
+ if (record == null) {
+ Log.w(TAG, "DiscoveryProviderManager cannot find the scan record.");
+ continue;
+ }
+ CallerIdentity callerIdentity = record.getCallerIdentity();
+ if (!DiscoveryPermissions.noteDiscoveryResultDelivery(
+ appOpsManager, callerIdentity)) {
+ Log.w(TAG, "[DiscoveryProviderManager] scan permission revoked "
+ + "- not forwarding results");
+ try {
+ record.getScanListener().onError(ScanCallback.ERROR_PERMISSION_DENIED);
+ } catch (RemoteException e) {
+ Log.w(TAG, "DiscoveryProviderManager failed to report error.", e);
+ }
+ return;
+ }
+
+ try {
+ record.getScanListener().onError(errorCode);
+ } catch (RemoteException e) {
+ Log.w(TAG, "DiscoveryProviderManager failed to report onError.", e);
+ }
+ }
+ }
+ }
+
+ /** Called after boot completed. */
+ public void init() {
+ if (mInjector.getContextHubManager() != null) {
+ mChreDiscoveryProvider.init();
+ }
+ mChreDiscoveryProvider.getController().setListener(this);
+ }
+
+ /**
+ * Registers the listener in the manager and starts scan according to the requested scan mode.
+ */
+ @NearbyManager.ScanStatus
+ public int registerScanListener(ScanRequest scanRequest, IScanListener listener,
+ CallerIdentity callerIdentity) {
+ synchronized (mLock) {
+ ScanListenerDeathRecipient deathRecipient = (listener != null)
+ ? new ScanListenerDeathRecipient(listener) : null;
+ IBinder listenerBinder = listener.asBinder();
+ if (listenerBinder != null && deathRecipient != null) {
+ try {
+ listenerBinder.linkToDeath(deathRecipient, 0);
+ } catch (RemoteException e) {
+ throw new IllegalArgumentException("Can't link to scan listener's death");
+ }
+ }
+ if (mScanTypeScanListenerRecordMap.containsKey(listener.asBinder())) {
+ ScanRequest savedScanRequest =
+ mScanTypeScanListenerRecordMap.get(listenerBinder).getScanRequest();
+ if (scanRequest.equals(savedScanRequest)) {
+ Log.d(TAG, "Already registered the scanRequest: " + scanRequest);
+ return NearbyManager.ScanStatus.SUCCESS;
+ }
+ }
+ ScanListenerRecord scanListenerRecord =
+ new ScanListenerRecord(scanRequest, listener, callerIdentity, deathRecipient);
+
+ Boolean started = startProviders(scanRequest);
+ if (started == null) {
+ return NearbyManager.ScanStatus.UNKNOWN;
+ }
+ if (!started) {
+ return NearbyManager.ScanStatus.ERROR;
+ }
+ mScanTypeScanListenerRecordMap.put(listenerBinder, scanListenerRecord);
+ NearbyMetrics.logScanStarted(scanListenerRecord.hashCode(), scanRequest);
+ if (mScanMode < scanRequest.getScanMode()) {
+ mScanMode = scanRequest.getScanMode();
+ invalidateProviderScanMode();
+ }
+ return NearbyManager.ScanStatus.SUCCESS;
+ }
+ }
+
+ /**
+ * 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(listenerBinder)) {
+ Log.w(
+ TAG,
+ "Cannot unregister the scanRequest because the request is never "
+ + "registered.");
+ return;
+ }
+
+ ScanListenerRecord removedRecord =
+ mScanTypeScanListenerRecordMap.remove(listenerBinder);
+ ScanListenerDeathRecipient deathRecipient = removedRecord.getDeathRecipient();
+ if (listenerBinder != null && deathRecipient != null) {
+ listenerBinder.unlinkToDeath(removedRecord.getDeathRecipient(), 0);
+ }
+ Log.v(TAG, "DiscoveryProviderManager unregistered scan listener.");
+ NearbyMetrics.logScanStopped(removedRecord.hashCode(), removedRecord.getScanRequest());
+ if (mScanTypeScanListenerRecordMap.isEmpty()) {
+ Log.v(TAG, "DiscoveryProviderManager stops provider because there is no "
+ + "scan listener registered.");
+ stopProviders();
+ return;
+ }
+
+ // TODO(b/221082271): updates the scan with reduced filters.
+
+ // Removes current highest scan mode requested and sets the next highest scan mode.
+ if (removedRecord.getScanRequest().getScanMode() == mScanMode) {
+ Log.v(TAG, "DiscoveryProviderManager starts to find the new highest scan mode "
+ + "because the highest scan mode listener was unregistered.");
+ @ScanRequest.ScanMode int highestScanModeRequested = ScanRequest.SCAN_MODE_NO_POWER;
+ // find the next highest scan mode;
+ for (ScanListenerRecord record : mScanTypeScanListenerRecordMap.values()) {
+ @ScanRequest.ScanMode int scanMode = record.getScanRequest().getScanMode();
+ if (scanMode > highestScanModeRequested) {
+ highestScanModeRequested = scanMode;
+ }
+ }
+ if (mScanMode != highestScanModeRequested) {
+ mScanMode = highestScanModeRequested;
+ invalidateProviderScanMode();
+ }
+ }
+ }
+ }
+
+ /**
+ * Query offload capability in a device.
+ */
+ public void queryOffloadCapability(IOffloadCallback callback) {
+ mChreDiscoveryProvider.queryOffloadCapability(callback);
+ }
+
+ /**
+ * @return {@code null} when all providers are initializing
+ * {@code false} when fail to start all the providers
+ * {@code true} when any one of the provider starts successfully
+ */
+ @VisibleForTesting
+ @Nullable
+ Boolean startProviders(ScanRequest scanRequest) {
+ if (!scanRequest.isBleEnabled()) {
+ Log.w(TAG, "failed to start any provider because client disabled BLE");
+ return false;
+ }
+ List<ScanFilter> scanFilters = getPresenceScanFilters();
+ boolean chreOnly = isChreOnly(scanFilters);
+ Boolean chreAvailable = mChreDiscoveryProvider.available();
+ if (chreAvailable == null) {
+ if (chreOnly) {
+ Log.w(TAG, "client wants CHRE only and Nearby service is still querying CHRE"
+ + " status");
+ return null;
+ }
+ startBleProvider(scanFilters);
+ return true;
+ }
+
+ if (!chreAvailable) {
+ if (chreOnly) {
+ Log.w(TAG, "failed to start any provider because client wants CHRE only and CHRE"
+ + " is not available");
+ return false;
+ }
+ startBleProvider(scanFilters);
+ return true;
+ }
+
+ if (scanRequest.getScanType() == SCAN_TYPE_NEARBY_PRESENCE) {
+ startChreProvider(scanFilters);
+ return true;
+ }
+
+ startBleProvider(scanFilters);
+ return true;
+ }
+
+ private void startBleProvider(List<ScanFilter> scanFilters) {
+ if (!mBleDiscoveryProvider.getController().isStarted()) {
+ Log.d(TAG, "DiscoveryProviderManager starts Ble scanning.");
+ mBleDiscoveryProvider.getController().setListener(this);
+ mBleDiscoveryProvider.getController().setProviderScanMode(mScanMode);
+ mBleDiscoveryProvider.getController().setProviderScanFilters(scanFilters);
+ mBleDiscoveryProvider.getController().start();
+ }
+ }
+
+ @VisibleForTesting
+ void startChreProvider(List<ScanFilter> scanFilters) {
+ Log.d(TAG, "DiscoveryProviderManager starts CHRE scanning.");
+ mChreDiscoveryProvider.getController().setProviderScanFilters(scanFilters);
+ mChreDiscoveryProvider.getController().setProviderScanMode(mScanMode);
+ mChreDiscoveryProvider.getController().start();
+ }
+
+ private List<ScanFilter> getPresenceScanFilters() {
+ synchronized (mLock) {
+ List<ScanFilter> scanFilters = new ArrayList();
+ for (IBinder listenerBinder : mScanTypeScanListenerRecordMap.keySet()) {
+ ScanListenerRecord record = mScanTypeScanListenerRecordMap.get(listenerBinder);
+ List<ScanFilter> presenceFilters =
+ record.getScanRequest().getScanFilters().stream()
+ .filter(
+ scanFilter ->
+ scanFilter.getType() == SCAN_TYPE_NEARBY_PRESENCE)
+ .collect(Collectors.toList());
+ scanFilters.addAll(presenceFilters);
+ }
+ return scanFilters;
+ }
+ }
+
+ private void stopProviders() {
+ stopBleProvider();
+ stopChreProvider();
+ }
+
+ private void stopBleProvider() {
+ mBleDiscoveryProvider.getController().stop();
+ }
+
+ @VisibleForTesting
+ protected void stopChreProvider() {
+ mChreDiscoveryProvider.getController().stop();
+ }
+
+ @VisibleForTesting
+ void invalidateProviderScanMode() {
+ if (mBleDiscoveryProvider.getController().isStarted()) {
+ mBleDiscoveryProvider.getController().setProviderScanMode(mScanMode);
+ } else {
+ Log.d(
+ TAG,
+ "Skip invalidating BleDiscoveryProvider scan mode because the provider not "
+ + "started.");
+ }
+ }
+
+ @VisibleForTesting
+ static class ScanListenerRecord {
+
+ private final ScanRequest mScanRequest;
+
+ private final IScanListener mScanListener;
+
+ private final CallerIdentity mCallerIdentity;
+
+ private final ScanListenerDeathRecipient mDeathRecipient;
+
+ ScanListenerRecord(ScanRequest scanRequest, IScanListener iScanListener,
+ CallerIdentity callerIdentity, ScanListenerDeathRecipient deathRecipient) {
+ mScanListener = iScanListener;
+ mScanRequest = scanRequest;
+ mCallerIdentity = callerIdentity;
+ mDeathRecipient = deathRecipient;
+ }
+
+ IScanListener getScanListener() {
+ return mScanListener;
+ }
+
+ ScanRequest getScanRequest() {
+ return mScanRequest;
+ }
+
+ CallerIdentity getCallerIdentity() {
+ return mCallerIdentity;
+ }
+
+ ScanListenerDeathRecipient getDeathRecipient() {
+ return mDeathRecipient;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ScanListenerRecord) {
+ ScanListenerRecord otherScanListenerRecord = (ScanListenerRecord) other;
+ return Objects.equals(mScanRequest, otherScanListenerRecord.mScanRequest)
+ && Objects.equals(mScanListener, otherScanListenerRecord.mScanListener);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mScanListener, mScanRequest);
+ }
+ }
+
+ /**
+ * Class to make listener unregister after the binder is dead.
+ */
+ public class ScanListenerDeathRecipient implements IBinder.DeathRecipient {
+ public IScanListener listener;
+
+ ScanListenerDeathRecipient(IScanListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void binderDied() {
+ Log.d(TAG, "Binder is dead - unregistering scan listener");
+ unregisterScanListener(listener);
+ }
+ }
+}
diff --git a/nearby/service/java/com/android/server/nearby/managers/registration/DiscoveryRegistration.java b/nearby/service/java/com/android/server/nearby/managers/registration/DiscoveryRegistration.java
index 1cb70ac..bef319ec 100644
--- a/nearby/service/java/com/android/server/nearby/managers/registration/DiscoveryRegistration.java
+++ b/nearby/service/java/com/android/server/nearby/managers/registration/DiscoveryRegistration.java
@@ -243,6 +243,11 @@
}
+ /** Reports an error to the client. */
+ public ListenerOperation<IScanListener> reportError(@ScanCallback.ErrorCode int errorCode) {
+ return listener -> listener.onError(errorCode);
+ }
+
@Nullable
ListenerOperation<IScanListener> reportResult(@DiscoveryResult int result,
NearbyDeviceParcelable device, @Nullable Runnable successReportCallback) {
@@ -250,7 +255,7 @@
// NOTE: AppOps report has to be the last operation before delivering the result. Otherwise
// we may over-report when the discovery result doesn't end up being delivered.
if (!checkIdentity()) {
- return listener -> listener.onError(ScanCallback.ERROR_PERMISSION_DENIED);
+ return reportError(ScanCallback.ERROR_PERMISSION_DENIED);
}
return new ListenerOperation<>() {
diff --git a/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java b/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
index 0d46d3e..90c85ab 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
@@ -36,6 +36,7 @@
import android.content.Context;
import android.nearby.IScanListener;
import android.nearby.ScanRequest;
+import android.os.IBinder;
import android.provider.DeviceConfig;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -61,10 +62,14 @@
private IScanListener mScanListener;
@Mock
private AppOpsManager mMockAppOpsManager;
+ @Mock
+ private IBinder mIBinder;
@Before
public void setUp() {
initMocks(this);
+ when(mScanListener.asBinder()).thenReturn(mIBinder);
+
mUiAutomation.adoptShellPermissionIdentity(
READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG, BLUETOOTH_PRIVILEGED);
mContext = InstrumentationRegistry.getInstrumentation().getContext();
diff --git a/nearby/tests/unit/src/com/android/server/nearby/managers/DiscoveryProviderManagerLegacyTest.java b/nearby/tests/unit/src/com/android/server/nearby/managers/DiscoveryProviderManagerLegacyTest.java
new file mode 100644
index 0000000..aa0dad3
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/managers/DiscoveryProviderManagerLegacyTest.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2023 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.managers;
+
+import static android.nearby.PresenceCredential.IDENTITY_TYPE_PRIVATE;
+import static android.nearby.ScanRequest.SCAN_TYPE_NEARBY_PRESENCE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.nearby.DataElement;
+import android.nearby.IScanListener;
+import android.nearby.NearbyDeviceParcelable;
+import android.nearby.PresenceScanFilter;
+import android.nearby.PublicCredential;
+import android.nearby.ScanRequest;
+import android.os.IBinder;
+
+import com.android.server.nearby.injector.Injector;
+import com.android.server.nearby.provider.BleDiscoveryProvider;
+import com.android.server.nearby.provider.ChreCommunication;
+import com.android.server.nearby.provider.ChreDiscoveryProvider;
+import com.android.server.nearby.provider.DiscoveryProviderController;
+import com.android.server.nearby.util.identity.CallerIdentity;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Unit test for {@link DiscoveryProviderManagerLegacy} class.
+ */
+public class DiscoveryProviderManagerLegacyTest {
+ private static final int SCAN_MODE_CHRE_ONLY = 3;
+ private static final int DATA_TYPE_SCAN_MODE = 102;
+ private static final int UID = 1234;
+ private static final int PID = 5678;
+ private static final String PACKAGE_NAME = "android.nearby.test";
+ private static final int RSSI = -60;
+ @Mock
+ Injector mInjector;
+ @Mock
+ Context mContext;
+ @Mock
+ AppOpsManager mAppOpsManager;
+ @Mock
+ BleDiscoveryProvider mBleDiscoveryProvider;
+ @Mock
+ ChreDiscoveryProvider mChreDiscoveryProvider;
+ @Mock
+ DiscoveryProviderController mBluetoothController;
+ @Mock
+ DiscoveryProviderController mChreController;
+ @Mock
+ IScanListener mScanListener;
+ @Mock
+ CallerIdentity mCallerIdentity;
+ @Mock
+ DiscoveryProviderManagerLegacy.ScanListenerDeathRecipient mScanListenerDeathRecipient;
+ @Mock
+ IBinder mIBinder;
+ private DiscoveryProviderManagerLegacy mDiscoveryProviderManager;
+ private Map<IBinder, DiscoveryProviderManagerLegacy.ScanListenerRecord>
+ mScanTypeScanListenerRecordMap;
+
+ private static PresenceScanFilter getPresenceScanFilter() {
+ final byte[] secretId = new byte[]{1, 2, 3, 4};
+ final byte[] authenticityKey = new byte[]{0, 1, 1, 1};
+ final byte[] publicKey = new byte[]{1, 1, 2, 2};
+ final byte[] encryptedMetadata = new byte[]{1, 2, 3, 4, 5};
+ final byte[] metadataEncryptionKeyTag = new byte[]{1, 1, 3, 4, 5};
+
+ PublicCredential credential = new PublicCredential.Builder(
+ secretId, authenticityKey, publicKey, encryptedMetadata, metadataEncryptionKeyTag)
+ .setIdentityType(IDENTITY_TYPE_PRIVATE)
+ .build();
+
+ final int action = 123;
+ return new PresenceScanFilter.Builder()
+ .addCredential(credential)
+ .setMaxPathLoss(RSSI)
+ .addPresenceAction(action)
+ .build();
+ }
+
+ private static PresenceScanFilter getChreOnlyPresenceScanFilter() {
+ final byte[] secretId = new byte[]{1, 2, 3, 4};
+ final byte[] authenticityKey = new byte[]{0, 1, 1, 1};
+ final byte[] publicKey = new byte[]{1, 1, 2, 2};
+ final byte[] encryptedMetadata = new byte[]{1, 2, 3, 4, 5};
+ final byte[] metadataEncryptionKeyTag = new byte[]{1, 1, 3, 4, 5};
+
+ PublicCredential credential = new PublicCredential.Builder(
+ secretId, authenticityKey, publicKey, encryptedMetadata, metadataEncryptionKeyTag)
+ .setIdentityType(IDENTITY_TYPE_PRIVATE)
+ .build();
+
+ final int action = 123;
+ DataElement scanModeElement = new DataElement(DATA_TYPE_SCAN_MODE,
+ new byte[]{SCAN_MODE_CHRE_ONLY});
+ return new PresenceScanFilter.Builder()
+ .addCredential(credential)
+ .setMaxPathLoss(RSSI)
+ .addPresenceAction(action)
+ .addExtendedProperty(scanModeElement)
+ .build();
+ }
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mInjector.getAppOpsManager()).thenReturn(mAppOpsManager);
+ when(mBleDiscoveryProvider.getController()).thenReturn(mBluetoothController);
+ when(mChreDiscoveryProvider.getController()).thenReturn(mChreController);
+
+ mScanTypeScanListenerRecordMap = new HashMap<>();
+ mDiscoveryProviderManager =
+ new DiscoveryProviderManagerLegacy(mContext, mInjector,
+ mBleDiscoveryProvider,
+ mChreDiscoveryProvider,
+ mScanTypeScanListenerRecordMap);
+ mCallerIdentity = CallerIdentity
+ .forTest(UID, PID, PACKAGE_NAME, /* attributionTag= */ null);
+ }
+
+ @Test
+ public void testOnNearbyDeviceDiscovered() {
+ NearbyDeviceParcelable nearbyDeviceParcelable = new NearbyDeviceParcelable.Builder()
+ .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+ .build();
+ mDiscoveryProviderManager.onNearbyDeviceDiscovered(nearbyDeviceParcelable);
+ }
+
+ @Test
+ public void testInvalidateProviderScanMode() {
+ mDiscoveryProviderManager.invalidateProviderScanMode();
+ }
+
+ @Test
+ public void testStartProviders_chreOnlyChreAvailable_bleProviderNotStarted() {
+ when(mChreDiscoveryProvider.available()).thenReturn(true);
+
+ ScanRequest scanRequest = new ScanRequest.Builder()
+ .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+ .addScanFilter(getChreOnlyPresenceScanFilter()).build();
+ DiscoveryProviderManagerLegacy.ScanListenerRecord record =
+ new DiscoveryProviderManagerLegacy.ScanListenerRecord(
+ scanRequest, mScanListener,
+ mCallerIdentity, mScanListenerDeathRecipient);
+ mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+ Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ verify(mBluetoothController, never()).start();
+ assertThat(start).isTrue();
+ }
+
+ @Test
+ public void testStartProviders_chreOnlyChreAvailable_multipleFilters_bleProviderNotStarted() {
+ when(mChreDiscoveryProvider.available()).thenReturn(true);
+
+ ScanRequest scanRequest = new ScanRequest.Builder()
+ .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+ .addScanFilter(getChreOnlyPresenceScanFilter())
+ .addScanFilter(getPresenceScanFilter()).build();
+ DiscoveryProviderManagerLegacy.ScanListenerRecord record =
+ new DiscoveryProviderManagerLegacy.ScanListenerRecord(
+ scanRequest, mScanListener,
+ mCallerIdentity, mScanListenerDeathRecipient);
+ mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+ Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ verify(mBluetoothController, never()).start();
+ assertThat(start).isTrue();
+ }
+
+ @Test
+ public void testStartProviders_chreOnlyChreUnavailable_bleProviderNotStarted() {
+ when(mChreDiscoveryProvider.available()).thenReturn(false);
+
+ ScanRequest scanRequest = new ScanRequest.Builder()
+ .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+ .addScanFilter(getChreOnlyPresenceScanFilter()).build();
+ DiscoveryProviderManagerLegacy.ScanListenerRecord record =
+ new DiscoveryProviderManagerLegacy.ScanListenerRecord(
+ scanRequest, mScanListener,
+ mCallerIdentity, mScanListenerDeathRecipient);
+ mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+ Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ verify(mBluetoothController, never()).start();
+ assertThat(start).isFalse();
+ }
+
+ @Test
+ public void testStartProviders_notChreOnlyChreAvailable_bleProviderNotStarted() {
+ when(mChreDiscoveryProvider.available()).thenReturn(true);
+
+ ScanRequest scanRequest = new ScanRequest.Builder()
+ .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+ .addScanFilter(getPresenceScanFilter()).build();
+ DiscoveryProviderManagerLegacy.ScanListenerRecord record =
+ new DiscoveryProviderManagerLegacy.ScanListenerRecord(
+ scanRequest, mScanListener,
+ mCallerIdentity, mScanListenerDeathRecipient);
+ mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+ Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ verify(mBluetoothController, never()).start();
+ assertThat(start).isTrue();
+ }
+
+ @Test
+ public void testStartProviders_notChreOnlyChreUnavailable_bleProviderStarted() {
+ when(mChreDiscoveryProvider.available()).thenReturn(false);
+
+ ScanRequest scanRequest = new ScanRequest.Builder()
+ .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+ .addScanFilter(getPresenceScanFilter()).build();
+ DiscoveryProviderManagerLegacy.ScanListenerRecord record =
+ new DiscoveryProviderManagerLegacy.ScanListenerRecord(
+ scanRequest, mScanListener,
+ mCallerIdentity, mScanListenerDeathRecipient);
+ mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+ Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ verify(mBluetoothController, atLeastOnce()).start();
+ assertThat(start).isTrue();
+ }
+
+ @Test
+ public void testStartProviders_chreOnlyChreUndetermined_bleProviderNotStarted() {
+ when(mChreDiscoveryProvider.available()).thenReturn(null);
+
+ ScanRequest scanRequest = new ScanRequest.Builder()
+ .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+ .addScanFilter(getChreOnlyPresenceScanFilter()).build();
+ DiscoveryProviderManagerLegacy.ScanListenerRecord record =
+ new DiscoveryProviderManagerLegacy.ScanListenerRecord(
+ scanRequest, mScanListener,
+ mCallerIdentity, mScanListenerDeathRecipient);
+ mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+ Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ verify(mBluetoothController, never()).start();
+ assertThat(start).isNull();
+ }
+
+ @Test
+ public void testStartProviders_notChreOnlyChreUndetermined_bleProviderStarted() {
+ when(mChreDiscoveryProvider.available()).thenReturn(null);
+
+ ScanRequest scanRequest = new ScanRequest.Builder()
+ .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+ .addScanFilter(getPresenceScanFilter()).build();
+ DiscoveryProviderManagerLegacy.ScanListenerRecord record =
+ new DiscoveryProviderManagerLegacy.ScanListenerRecord(
+ scanRequest, mScanListener,
+ mCallerIdentity, mScanListenerDeathRecipient);
+ mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+ Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ verify(mBluetoothController, atLeastOnce()).start();
+ assertThat(start).isTrue();
+ }
+
+ @Test
+ public void test_stopChreProvider_clearFilters() throws Exception {
+ // Cannot use mocked ChreDiscoveryProvider,
+ // so we cannot use class variable mDiscoveryProviderManager
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ DiscoveryProviderManagerLegacy manager =
+ new DiscoveryProviderManagerLegacy(mContext, mInjector,
+ mBleDiscoveryProvider,
+ new ChreDiscoveryProvider(
+ mContext,
+ new ChreCommunication(mInjector, mContext, executor), executor),
+ mScanTypeScanListenerRecordMap);
+ ScanRequest scanRequest = new ScanRequest.Builder()
+ .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+ .addScanFilter(getPresenceScanFilter()).build();
+ DiscoveryProviderManagerLegacy.ScanListenerRecord record =
+ new DiscoveryProviderManagerLegacy.ScanListenerRecord(
+ scanRequest, mScanListener,
+ mCallerIdentity, mScanListenerDeathRecipient);
+ mScanTypeScanListenerRecordMap.put(mIBinder, record);
+ manager.startChreProvider(List.of(getPresenceScanFilter()));
+ // This is an asynchronized process. The filters will be set in executor thread. So we need
+ // to wait for some time to get the correct result.
+ Thread.sleep(200);
+
+ assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
+ .isTrue();
+ assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
+
+ manager.stopChreProvider();
+ Thread.sleep(200);
+ // The filters should be cleared right after.
+ assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
+ .isFalse();
+ assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isEmpty();
+ }
+
+ @Test
+ public void test_restartChreProvider() throws Exception {
+ // Cannot use mocked ChreDiscoveryProvider,
+ // so we cannot use class variable mDiscoveryProviderManager
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ DiscoveryProviderManagerLegacy manager =
+ new DiscoveryProviderManagerLegacy(mContext, mInjector,
+ mBleDiscoveryProvider,
+ new ChreDiscoveryProvider(
+ mContext,
+ new ChreCommunication(mInjector, mContext, executor), executor),
+ mScanTypeScanListenerRecordMap);
+ ScanRequest scanRequest = new ScanRequest.Builder()
+ .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+ .addScanFilter(getPresenceScanFilter()).build();
+ DiscoveryProviderManagerLegacy.ScanListenerRecord record =
+ new DiscoveryProviderManagerLegacy.ScanListenerRecord(scanRequest, mScanListener,
+ mCallerIdentity, mScanListenerDeathRecipient);
+ mScanTypeScanListenerRecordMap.put(mIBinder, record);
+ manager.startChreProvider(List.of(getPresenceScanFilter()));
+ // This is an asynchronized process. The filters will be set in executor thread. So we need
+ // to wait for some time to get the correct result.
+ Thread.sleep(200);
+
+ assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
+ .isTrue();
+ assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
+
+ // We want to make sure quickly restart the provider the filters should
+ // be reset correctly.
+ // See b/255922206, there can be a race condition that filters get cleared because onStop()
+ // get executed after onStart() if they are called from different threads.
+ manager.stopChreProvider();
+ manager.mChreDiscoveryProvider.getController().setProviderScanFilters(
+ List.of(getPresenceScanFilter()));
+ manager.startChreProvider(List.of(getPresenceScanFilter()));
+ Thread.sleep(200);
+ assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
+ .isTrue();
+ assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
+
+ // Wait for enough time
+ Thread.sleep(1000);
+
+ assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
+ .isTrue();
+ assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/managers/DiscoveryProviderManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/managers/DiscoveryProviderManagerTest.java
index 542ceed..7ecf631 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/managers/DiscoveryProviderManagerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/managers/DiscoveryProviderManagerTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -48,10 +49,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class DiscoveryProviderManagerTest {
@@ -80,12 +79,9 @@
@Mock
CallerIdentity mCallerIdentity;
@Mock
- DiscoveryProviderManager.ScanListenerDeathRecipient mScanListenerDeathRecipient;
- @Mock
IBinder mIBinder;
+ private Executor mExecutor;
private DiscoveryProviderManager mDiscoveryProviderManager;
- private Map<IBinder, DiscoveryProviderManager.ScanListenerRecord>
- mScanTypeScanListenerRecordMap;
private static PresenceScanFilter getPresenceScanFilter() {
final byte[] secretId = new byte[]{1, 2, 3, 4};
@@ -133,16 +129,16 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ mExecutor = Executors.newSingleThreadExecutor();
when(mInjector.getAppOpsManager()).thenReturn(mAppOpsManager);
when(mBleDiscoveryProvider.getController()).thenReturn(mBluetoothController);
when(mChreDiscoveryProvider.getController()).thenReturn(mChreController);
+ when(mScanListener.asBinder()).thenReturn(mIBinder);
- mScanTypeScanListenerRecordMap = new HashMap<>();
mDiscoveryProviderManager =
- new DiscoveryProviderManager(mContext, mInjector,
+ new DiscoveryProviderManager(mContext, mExecutor, mInjector,
mBleDiscoveryProvider,
- mChreDiscoveryProvider,
- mScanTypeScanListenerRecordMap);
+ mChreDiscoveryProvider);
mCallerIdentity = CallerIdentity
.forTest(UID, PID, PACKAGE_NAME, /* attributionTag= */ null);
}
@@ -162,55 +158,45 @@
@Test
public void testStartProviders_chreOnlyChreAvailable_bleProviderNotStarted() {
+ reset(mBluetoothController);
when(mChreDiscoveryProvider.available()).thenReturn(true);
ScanRequest scanRequest = new ScanRequest.Builder()
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
.addScanFilter(getChreOnlyPresenceScanFilter()).build();
- DiscoveryProviderManager.ScanListenerRecord record =
- new DiscoveryProviderManager.ScanListenerRecord(
- scanRequest, mScanListener,
- mCallerIdentity, mScanListenerDeathRecipient);
- mScanTypeScanListenerRecordMap.put(mIBinder, record);
+ mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
- Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ Boolean start = mDiscoveryProviderManager.startProviders();
verify(mBluetoothController, never()).start();
assertThat(start).isTrue();
}
@Test
public void testStartProviders_chreOnlyChreAvailable_multipleFilters_bleProviderNotStarted() {
+ reset(mBluetoothController);
when(mChreDiscoveryProvider.available()).thenReturn(true);
ScanRequest scanRequest = new ScanRequest.Builder()
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
- .addScanFilter(getChreOnlyPresenceScanFilter())
- .addScanFilter(getPresenceScanFilter()).build();
- DiscoveryProviderManager.ScanListenerRecord record =
- new DiscoveryProviderManager.ScanListenerRecord(
- scanRequest, mScanListener,
- mCallerIdentity, mScanListenerDeathRecipient);
- mScanTypeScanListenerRecordMap.put(mIBinder, record);
+ .addScanFilter(getChreOnlyPresenceScanFilter()).build();
+ mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
- Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ Boolean start = mDiscoveryProviderManager.startProviders();
verify(mBluetoothController, never()).start();
assertThat(start).isTrue();
}
@Test
public void testStartProviders_chreOnlyChreUnavailable_bleProviderNotStarted() {
+ reset(mBluetoothController);
when(mChreDiscoveryProvider.available()).thenReturn(false);
ScanRequest scanRequest = new ScanRequest.Builder()
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
.addScanFilter(getChreOnlyPresenceScanFilter()).build();
- DiscoveryProviderManager.ScanListenerRecord record =
- new DiscoveryProviderManager.ScanListenerRecord(
- scanRequest, mScanListener,
- mCallerIdentity, mScanListenerDeathRecipient);
- mScanTypeScanListenerRecordMap.put(mIBinder, record);
+ mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
- Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ Boolean start = mDiscoveryProviderManager.startProviders();
verify(mBluetoothController, never()).start();
assertThat(start).isFalse();
}
@@ -218,17 +204,14 @@
@Test
public void testStartProviders_notChreOnlyChreAvailable_bleProviderNotStarted() {
when(mChreDiscoveryProvider.available()).thenReturn(true);
+ reset(mBluetoothController);
ScanRequest scanRequest = new ScanRequest.Builder()
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
.addScanFilter(getPresenceScanFilter()).build();
- DiscoveryProviderManager.ScanListenerRecord record =
- new DiscoveryProviderManager.ScanListenerRecord(
- scanRequest, mScanListener,
- mCallerIdentity, mScanListenerDeathRecipient);
- mScanTypeScanListenerRecordMap.put(mIBinder, record);
+ mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
- Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ Boolean start = mDiscoveryProviderManager.startProviders();
verify(mBluetoothController, never()).start();
assertThat(start).isTrue();
}
@@ -236,17 +219,14 @@
@Test
public void testStartProviders_notChreOnlyChreUnavailable_bleProviderStarted() {
when(mChreDiscoveryProvider.available()).thenReturn(false);
+ reset(mBluetoothController);
ScanRequest scanRequest = new ScanRequest.Builder()
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
.addScanFilter(getPresenceScanFilter()).build();
- DiscoveryProviderManager.ScanListenerRecord record =
- new DiscoveryProviderManager.ScanListenerRecord(
- scanRequest, mScanListener,
- mCallerIdentity, mScanListenerDeathRecipient);
- mScanTypeScanListenerRecordMap.put(mIBinder, record);
+ mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
- Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ Boolean start = mDiscoveryProviderManager.startProviders();
verify(mBluetoothController, atLeastOnce()).start();
assertThat(start).isTrue();
}
@@ -254,17 +234,14 @@
@Test
public void testStartProviders_chreOnlyChreUndetermined_bleProviderNotStarted() {
when(mChreDiscoveryProvider.available()).thenReturn(null);
+ reset(mBluetoothController);
ScanRequest scanRequest = new ScanRequest.Builder()
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
.addScanFilter(getChreOnlyPresenceScanFilter()).build();
- DiscoveryProviderManager.ScanListenerRecord record =
- new DiscoveryProviderManager.ScanListenerRecord(
- scanRequest, mScanListener,
- mCallerIdentity, mScanListenerDeathRecipient);
- mScanTypeScanListenerRecordMap.put(mIBinder, record);
+ mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
- Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ Boolean start = mDiscoveryProviderManager.startProviders();
verify(mBluetoothController, never()).start();
assertThat(start).isNull();
}
@@ -272,17 +249,14 @@
@Test
public void testStartProviders_notChreOnlyChreUndetermined_bleProviderStarted() {
when(mChreDiscoveryProvider.available()).thenReturn(null);
+ reset(mBluetoothController);
ScanRequest scanRequest = new ScanRequest.Builder()
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
.addScanFilter(getPresenceScanFilter()).build();
- DiscoveryProviderManager.ScanListenerRecord record =
- new DiscoveryProviderManager.ScanListenerRecord(
- scanRequest, mScanListener,
- mCallerIdentity, mScanListenerDeathRecipient);
- mScanTypeScanListenerRecordMap.put(mIBinder, record);
+ mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
- Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+ Boolean start = mDiscoveryProviderManager.startProviders();
verify(mBluetoothController, atLeastOnce()).start();
assertThat(start).isTrue();
}
@@ -291,22 +265,16 @@
public void test_stopChreProvider_clearFilters() throws Exception {
// Cannot use mocked ChreDiscoveryProvider,
// so we cannot use class variable mDiscoveryProviderManager
- ExecutorService executor = Executors.newSingleThreadExecutor();
DiscoveryProviderManager manager =
- new DiscoveryProviderManager(mContext, mInjector,
+ new DiscoveryProviderManager(mContext, mExecutor, mInjector,
mBleDiscoveryProvider,
new ChreDiscoveryProvider(
mContext,
- new ChreCommunication(mInjector, mContext, executor), executor),
- mScanTypeScanListenerRecordMap);
+ new ChreCommunication(mInjector, mContext, mExecutor), mExecutor));
ScanRequest scanRequest = new ScanRequest.Builder()
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
.addScanFilter(getPresenceScanFilter()).build();
- DiscoveryProviderManager.ScanListenerRecord record =
- new DiscoveryProviderManager.ScanListenerRecord(
- scanRequest, mScanListener,
- mCallerIdentity, mScanListenerDeathRecipient);
- mScanTypeScanListenerRecordMap.put(mIBinder, record);
+ manager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
manager.startChreProvider(List.of(getPresenceScanFilter()));
// This is an asynchronized process. The filters will be set in executor thread. So we need
// to wait for some time to get the correct result.
@@ -328,21 +296,17 @@
public void test_restartChreProvider() throws Exception {
// Cannot use mocked ChreDiscoveryProvider,
// so we cannot use class variable mDiscoveryProviderManager
- ExecutorService executor = Executors.newSingleThreadExecutor();
DiscoveryProviderManager manager =
- new DiscoveryProviderManager(mContext, mInjector,
+ new DiscoveryProviderManager(mContext, mExecutor, mInjector,
mBleDiscoveryProvider,
new ChreDiscoveryProvider(
mContext,
- new ChreCommunication(mInjector, mContext, executor), executor),
- mScanTypeScanListenerRecordMap);
+ new ChreCommunication(mInjector, mContext, mExecutor), mExecutor));
ScanRequest scanRequest = new ScanRequest.Builder()
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
.addScanFilter(getPresenceScanFilter()).build();
- DiscoveryProviderManager.ScanListenerRecord record =
- new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
- mCallerIdentity, mScanListenerDeathRecipient);
- mScanTypeScanListenerRecordMap.put(mIBinder, record);
+ manager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
+
manager.startChreProvider(List.of(getPresenceScanFilter()));
// This is an asynchronized process. The filters will be set in executor thread. So we need
// to wait for some time to get the correct result.