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
This commit is contained in:
parent
183b7d11a6
commit
57bd7320e4
8 changed files with 1150 additions and 423 deletions
|
|
@ -45,7 +45,9 @@ import com.android.server.nearby.common.locator.LocatorContextWrapper;
|
|||
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 @@ public class NearbyService extends INearbyManager.Stub {
|
|||
}
|
||||
}
|
||||
};
|
||||
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 @@ public class NearbyService extends INearbyManager.Stub {
|
|||
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 @@ public class NearbyService extends INearbyManager.Stub {
|
|||
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
|
||||
DiscoveryPermissions.enforceDiscoveryPermission(mContext, identity);
|
||||
|
||||
mProviderManager.unregisterScanListener(listener);
|
||||
mDiscoveryProviderManager.unregisterScanListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -147,7 +152,7 @@ public class NearbyService extends INearbyManager.Stub {
|
|||
|
||||
@Override
|
||||
public void queryOffloadCapability(IOffloadCallback callback) {
|
||||
mProviderManager.queryOffloadCapability(callback);
|
||||
mDiscoveryProviderManager.queryOffloadCapability(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -174,7 +179,7 @@ public class NearbyService extends INearbyManager.Stub {
|
|||
// Initialize ContextManager for CHRE scan.
|
||||
((SystemInjector) mInjector).initializeContextHubManager();
|
||||
}
|
||||
mProviderManager.init();
|
||||
mDiscoveryProviderManager.init();
|
||||
mContext.registerReceiver(
|
||||
mBluetoothReceiver,
|
||||
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -21,320 +21,74 @@ 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 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 @@ public class DiscoveryProviderManager implements AbstractDiscoveryProvider.Liste
|
|||
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 @@ public class DiscoveryProviderManager implements AbstractDiscoveryProvider.Liste
|
|||
@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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -243,6 +243,11 @@ public class DiscoveryRegistration extends BinderListenerRegistration<IScanListe
|
|||
|
||||
}
|
||||
|
||||
/** 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 @@ public class DiscoveryRegistration extends BinderListenerRegistration<IScanListe
|
|||
// 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<>() {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import android.app.UiAutomation;
|
|||
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 @@ public final class NearbyServiceTest {
|
|||
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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ 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.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
|
@ -48,10 +49,8 @@ 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.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class DiscoveryProviderManagerTest {
|
||||
|
|
@ -80,12 +79,9 @@ public class DiscoveryProviderManagerTest {
|
|||
@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 @@ public class DiscoveryProviderManagerTest {
|
|||
@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 @@ public class DiscoveryProviderManagerTest {
|
|||
|
||||
@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 @@ public class DiscoveryProviderManagerTest {
|
|||
@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 @@ public class DiscoveryProviderManagerTest {
|
|||
@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 @@ public class DiscoveryProviderManagerTest {
|
|||
@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 @@ public class DiscoveryProviderManagerTest {
|
|||
@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 class DiscoveryProviderManagerTest {
|
|||
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 class DiscoveryProviderManagerTest {
|
|||
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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue