Merge "Change the DiscoveryProviderManager to use the Multiplexer logic" into udc-dev am: 33d8a5305d
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/22881045 Change-Id: I6980900653ebcdfc7bd82583208b047e097dbc7c Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
7faa4f5708
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.fastpair.FastPairManager;
|
||||||
import com.android.server.nearby.injector.Injector;
|
import com.android.server.nearby.injector.Injector;
|
||||||
import com.android.server.nearby.managers.BroadcastProviderManager;
|
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.DiscoveryProviderManager;
|
||||||
|
import com.android.server.nearby.managers.DiscoveryProviderManagerLegacy;
|
||||||
import com.android.server.nearby.presence.PresenceManager;
|
import com.android.server.nearby.presence.PresenceManager;
|
||||||
import com.android.server.nearby.provider.FastPairDataProvider;
|
import com.android.server.nearby.provider.FastPairDataProvider;
|
||||||
import com.android.server.nearby.util.identity.CallerIdentity;
|
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;
|
private final BroadcastProviderManager mBroadcastProviderManager;
|
||||||
|
|
||||||
public NearbyService(Context context) {
|
public NearbyService(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mInjector = new SystemInjector(context);
|
mInjector = new SystemInjector(context);
|
||||||
mProviderManager = new DiscoveryProviderManager(context, mInjector);
|
|
||||||
mBroadcastProviderManager = new BroadcastProviderManager(context, mInjector);
|
mBroadcastProviderManager = new BroadcastProviderManager(context, mInjector);
|
||||||
final LocatorContextWrapper lcw = new LocatorContextWrapper(context, null);
|
final LocatorContextWrapper lcw = new LocatorContextWrapper(context, null);
|
||||||
mFastPairManager = new FastPairManager(lcw);
|
mFastPairManager = new FastPairManager(lcw);
|
||||||
mPresenceManager = new PresenceManager(lcw);
|
mPresenceManager = new PresenceManager(lcw);
|
||||||
mNearbyConfiguration = new NearbyConfiguration();
|
mNearbyConfiguration = new NearbyConfiguration();
|
||||||
|
mDiscoveryProviderManager =
|
||||||
|
mNearbyConfiguration.refactorDiscoveryManager()
|
||||||
|
? new DiscoveryProviderManager(context, mInjector)
|
||||||
|
: new DiscoveryProviderManagerLegacy(context, mInjector);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
@ -108,7 +113,7 @@ public class NearbyService extends INearbyManager.Stub {
|
||||||
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
|
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
|
||||||
DiscoveryPermissions.enforceDiscoveryPermission(mContext, identity);
|
DiscoveryPermissions.enforceDiscoveryPermission(mContext, identity);
|
||||||
|
|
||||||
return mProviderManager.registerScanListener(scanRequest, listener, identity);
|
return mDiscoveryProviderManager.registerScanListener(scanRequest, listener, identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -119,7 +124,7 @@ public class NearbyService extends INearbyManager.Stub {
|
||||||
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
|
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
|
||||||
DiscoveryPermissions.enforceDiscoveryPermission(mContext, identity);
|
DiscoveryPermissions.enforceDiscoveryPermission(mContext, identity);
|
||||||
|
|
||||||
mProviderManager.unregisterScanListener(listener);
|
mDiscoveryProviderManager.unregisterScanListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -147,7 +152,7 @@ public class NearbyService extends INearbyManager.Stub {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queryOffloadCapability(IOffloadCallback callback) {
|
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.
|
// Initialize ContextManager for CHRE scan.
|
||||||
((SystemInjector) mInjector).initializeContextHubManager();
|
((SystemInjector) mInjector).initializeContextHubManager();
|
||||||
}
|
}
|
||||||
mProviderManager.init();
|
mDiscoveryProviderManager.init();
|
||||||
mContext.registerReceiver(
|
mContext.registerReceiver(
|
||||||
mBluetoothReceiver,
|
mBluetoothReceiver,
|
||||||
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
|
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 static com.android.server.nearby.NearbyService.TAG;
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.AppOpsManager;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.nearby.DataElement;
|
import android.nearby.DataElement;
|
||||||
import android.nearby.IScanListener;
|
import android.nearby.IScanListener;
|
||||||
import android.nearby.NearbyDeviceParcelable;
|
import android.nearby.NearbyDeviceParcelable;
|
||||||
import android.nearby.NearbyManager;
|
import android.nearby.NearbyManager;
|
||||||
import android.nearby.PresenceScanFilter;
|
import android.nearby.PresenceScanFilter;
|
||||||
import android.nearby.ScanCallback;
|
|
||||||
import android.nearby.ScanFilter;
|
import android.nearby.ScanFilter;
|
||||||
import android.nearby.ScanRequest;
|
import android.nearby.ScanRequest;
|
||||||
import android.nearby.aidl.IOffloadCallback;
|
import android.nearby.aidl.IOffloadCallback;
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.server.nearby.injector.Injector;
|
import com.android.server.nearby.injector.Injector;
|
||||||
import com.android.server.nearby.metrics.NearbyMetrics;
|
import com.android.server.nearby.managers.registration.DiscoveryRegistration;
|
||||||
import com.android.server.nearby.presence.PresenceDiscoveryResult;
|
|
||||||
import com.android.server.nearby.provider.AbstractDiscoveryProvider;
|
import com.android.server.nearby.provider.AbstractDiscoveryProvider;
|
||||||
import com.android.server.nearby.provider.BleDiscoveryProvider;
|
import com.android.server.nearby.provider.BleDiscoveryProvider;
|
||||||
import com.android.server.nearby.provider.ChreCommunication;
|
import com.android.server.nearby.provider.ChreCommunication;
|
||||||
import com.android.server.nearby.provider.ChreDiscoveryProvider;
|
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.identity.CallerIdentity;
|
||||||
import com.android.server.nearby.util.permissions.DiscoveryPermissions;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Set;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
|
||||||
/** Manages all aspects of discovery providers. */
|
/** 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();
|
protected final Object mLock = new Object();
|
||||||
private final Context mContext;
|
|
||||||
private final BleDiscoveryProvider mBleDiscoveryProvider;
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@Nullable
|
@Nullable
|
||||||
final ChreDiscoveryProvider mChreDiscoveryProvider;
|
final ChreDiscoveryProvider mChreDiscoveryProvider;
|
||||||
private @ScanRequest.ScanMode int mScanMode;
|
private final Context mContext;
|
||||||
|
private final BleDiscoveryProvider mBleDiscoveryProvider;
|
||||||
private final Injector mInjector;
|
private final Injector mInjector;
|
||||||
|
private final Executor mExecutor;
|
||||||
@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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiscoveryProviderManager(Context context, Injector injector) {
|
public DiscoveryProviderManager(Context context, Injector injector) {
|
||||||
|
Log.v(TAG, "DiscoveryProviderManager: ");
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mBleDiscoveryProvider = new BleDiscoveryProvider(mContext, injector);
|
mBleDiscoveryProvider = new BleDiscoveryProvider(mContext, injector);
|
||||||
Executor executor = Executors.newSingleThreadExecutor();
|
mExecutor = Executors.newSingleThreadExecutor();
|
||||||
mChreDiscoveryProvider =
|
mChreDiscoveryProvider = new ChreDiscoveryProvider(mContext,
|
||||||
new ChreDiscoveryProvider(
|
new ChreCommunication(injector, mContext, mExecutor), mExecutor);
|
||||||
mContext, new ChreCommunication(injector, mContext, executor), executor);
|
|
||||||
mScanTypeScanListenerRecordMap = new HashMap<>();
|
|
||||||
mInjector = injector;
|
mInjector = injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
DiscoveryProviderManager(Context context, Injector injector,
|
DiscoveryProviderManager(Context context, Executor executor, Injector injector,
|
||||||
BleDiscoveryProvider bleDiscoveryProvider,
|
BleDiscoveryProvider bleDiscoveryProvider,
|
||||||
ChreDiscoveryProvider chreDiscoveryProvider,
|
ChreDiscoveryProvider chreDiscoveryProvider) {
|
||||||
Map<IBinder, ScanListenerRecord> scanTypeScanListenerRecordMap) {
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
mExecutor = executor;
|
||||||
mInjector = injector;
|
mInjector = injector;
|
||||||
mBleDiscoveryProvider = bleDiscoveryProvider;
|
mBleDiscoveryProvider = bleDiscoveryProvider;
|
||||||
mChreDiscoveryProvider = chreDiscoveryProvider;
|
mChreDiscoveryProvider = chreDiscoveryProvider;
|
||||||
mScanTypeScanListenerRecordMap = scanTypeScanListenerRecordMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called after boot completed. */
|
private static boolean isChreOnly(Set<ScanFilter> scanFilters) {
|
||||||
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) {
|
|
||||||
for (ScanFilter scanFilter : scanFilters) {
|
for (ScanFilter scanFilter : scanFilters) {
|
||||||
List<DataElement> dataElements =
|
List<DataElement> dataElements =
|
||||||
((PresenceScanFilter) scanFilter).getExtendedProperties();
|
((PresenceScanFilter) scanFilter).getExtendedProperties();
|
||||||
|
|
@ -355,41 +109,160 @@ public class DiscoveryProviderManager implements AbstractDiscoveryProvider.Liste
|
||||||
return false;
|
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()) {
|
if (!mBleDiscoveryProvider.getController().isStarted()) {
|
||||||
Log.d(TAG, "DiscoveryProviderManager starts Ble scanning.");
|
Log.d(TAG, "DiscoveryProviderManager starts Ble scanning.");
|
||||||
mBleDiscoveryProvider.getController().setListener(this);
|
mBleDiscoveryProvider.getController().setListener(this);
|
||||||
mBleDiscoveryProvider.getController().setProviderScanMode(mScanMode);
|
mBleDiscoveryProvider.getController().setProviderScanMode(mMerged.getScanMode());
|
||||||
mBleDiscoveryProvider.getController().setProviderScanFilters(scanFilters);
|
mBleDiscoveryProvider.getController().setProviderScanFilters(
|
||||||
|
new ArrayList<>(scanFilters));
|
||||||
mBleDiscoveryProvider.getController().start();
|
mBleDiscoveryProvider.getController().start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void startChreProvider(List<ScanFilter> scanFilters) {
|
@GuardedBy("mMultiplexerLock")
|
||||||
Log.d(TAG, "DiscoveryProviderManager starts CHRE scanning.");
|
void startChreProvider(Collection<ScanFilter> scanFilters) {
|
||||||
mChreDiscoveryProvider.getController().setProviderScanFilters(scanFilters);
|
Log.d(TAG, "DiscoveryProviderManager starts CHRE scanning. " + mMerged);
|
||||||
mChreDiscoveryProvider.getController().setProviderScanMode(mScanMode);
|
mChreDiscoveryProvider.getController().setProviderScanFilters(new ArrayList<>(scanFilters));
|
||||||
|
mChreDiscoveryProvider.getController().setProviderScanMode(mMerged.getScanMode());
|
||||||
mChreDiscoveryProvider.getController().start();
|
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() {
|
private void stopProviders() {
|
||||||
stopBleProvider();
|
stopBleProvider();
|
||||||
stopChreProvider();
|
stopChreProvider();
|
||||||
|
|
@ -407,96 +280,40 @@ public class DiscoveryProviderManager implements AbstractDiscoveryProvider.Liste
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void invalidateProviderScanMode() {
|
void invalidateProviderScanMode() {
|
||||||
if (mBleDiscoveryProvider.getController().isStarted()) {
|
if (mBleDiscoveryProvider.getController().isStarted()) {
|
||||||
mBleDiscoveryProvider.getController().setProviderScanMode(mScanMode);
|
synchronized (mMultiplexerLock) {
|
||||||
|
mBleDiscoveryProvider.getController().setProviderScanMode(mMerged.getScanMode());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.d(
|
Log.d(TAG, "Skip invalidating BleDiscoveryProvider scan mode because the provider not "
|
||||||
TAG,
|
+ "started.");
|
||||||
"Skip invalidating BleDiscoveryProvider scan mode because the provider not "
|
|
||||||
+ "started.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@Override
|
||||||
static boolean presenceFilterMatches(
|
public MergedDiscoveryRequest mergeRegistrations(
|
||||||
NearbyDeviceParcelable device, List<ScanFilter> scanFilters) {
|
@NonNull Collection<DiscoveryRegistration> registrations) {
|
||||||
if (scanFilters.isEmpty()) {
|
MergedDiscoveryRequest.Builder builder = new MergedDiscoveryRequest.Builder();
|
||||||
return true;
|
int scanMode = ScanRequest.SCAN_MODE_NO_POWER;
|
||||||
}
|
for (DiscoveryRegistration registration : registrations) {
|
||||||
PresenceDiscoveryResult discoveryResult = PresenceDiscoveryResult.fromDevice(device);
|
builder.addActions(registration.getActions());
|
||||||
for (ScanFilter scanFilter : scanFilters) {
|
builder.addScanFilters(registration.getPresenceScanFilters());
|
||||||
PresenceScanFilter presenceScanFilter = (PresenceScanFilter) scanFilter;
|
Log.d(TAG,
|
||||||
if (discoveryResult.matches(presenceScanFilter)) {
|
"mergeRegistrations: type is " + registration.getScanRequest().getScanType());
|
||||||
return true;
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Class to make listener unregister after the binder is dead.
|
public void onMergedRegistrationsUpdated() {
|
||||||
*/
|
invalidateProviderScanMode();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
@Nullable
|
||||||
ListenerOperation<IScanListener> reportResult(@DiscoveryResult int result,
|
ListenerOperation<IScanListener> reportResult(@DiscoveryResult int result,
|
||||||
NearbyDeviceParcelable device, @Nullable Runnable successReportCallback) {
|
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
|
// 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.
|
// we may over-report when the discovery result doesn't end up being delivered.
|
||||||
if (!checkIdentity()) {
|
if (!checkIdentity()) {
|
||||||
return listener -> listener.onError(ScanCallback.ERROR_PERMISSION_DENIED);
|
return reportError(ScanCallback.ERROR_PERMISSION_DENIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ListenerOperation<>() {
|
return new ListenerOperation<>() {
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import android.app.UiAutomation;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.nearby.IScanListener;
|
import android.nearby.IScanListener;
|
||||||
import android.nearby.ScanRequest;
|
import android.nearby.ScanRequest;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry;
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
|
@ -61,10 +62,14 @@ public final class NearbyServiceTest {
|
||||||
private IScanListener mScanListener;
|
private IScanListener mScanListener;
|
||||||
@Mock
|
@Mock
|
||||||
private AppOpsManager mMockAppOpsManager;
|
private AppOpsManager mMockAppOpsManager;
|
||||||
|
@Mock
|
||||||
|
private IBinder mIBinder;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
initMocks(this);
|
initMocks(this);
|
||||||
|
when(mScanListener.asBinder()).thenReturn(mIBinder);
|
||||||
|
|
||||||
mUiAutomation.adoptShellPermissionIdentity(
|
mUiAutomation.adoptShellPermissionIdentity(
|
||||||
READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG, BLUETOOTH_PRIVILEGED);
|
READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG, BLUETOOTH_PRIVILEGED);
|
||||||
mContext = InstrumentationRegistry.getInstrumentation().getContext();
|
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.atLeastOnce;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.reset;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
|
@ -48,10 +49,8 @@ import org.junit.Test;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class DiscoveryProviderManagerTest {
|
public class DiscoveryProviderManagerTest {
|
||||||
|
|
@ -80,12 +79,9 @@ public class DiscoveryProviderManagerTest {
|
||||||
@Mock
|
@Mock
|
||||||
CallerIdentity mCallerIdentity;
|
CallerIdentity mCallerIdentity;
|
||||||
@Mock
|
@Mock
|
||||||
DiscoveryProviderManager.ScanListenerDeathRecipient mScanListenerDeathRecipient;
|
|
||||||
@Mock
|
|
||||||
IBinder mIBinder;
|
IBinder mIBinder;
|
||||||
|
private Executor mExecutor;
|
||||||
private DiscoveryProviderManager mDiscoveryProviderManager;
|
private DiscoveryProviderManager mDiscoveryProviderManager;
|
||||||
private Map<IBinder, DiscoveryProviderManager.ScanListenerRecord>
|
|
||||||
mScanTypeScanListenerRecordMap;
|
|
||||||
|
|
||||||
private static PresenceScanFilter getPresenceScanFilter() {
|
private static PresenceScanFilter getPresenceScanFilter() {
|
||||||
final byte[] secretId = new byte[]{1, 2, 3, 4};
|
final byte[] secretId = new byte[]{1, 2, 3, 4};
|
||||||
|
|
@ -133,16 +129,16 @@ public class DiscoveryProviderManagerTest {
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mExecutor = Executors.newSingleThreadExecutor();
|
||||||
when(mInjector.getAppOpsManager()).thenReturn(mAppOpsManager);
|
when(mInjector.getAppOpsManager()).thenReturn(mAppOpsManager);
|
||||||
when(mBleDiscoveryProvider.getController()).thenReturn(mBluetoothController);
|
when(mBleDiscoveryProvider.getController()).thenReturn(mBluetoothController);
|
||||||
when(mChreDiscoveryProvider.getController()).thenReturn(mChreController);
|
when(mChreDiscoveryProvider.getController()).thenReturn(mChreController);
|
||||||
|
when(mScanListener.asBinder()).thenReturn(mIBinder);
|
||||||
|
|
||||||
mScanTypeScanListenerRecordMap = new HashMap<>();
|
|
||||||
mDiscoveryProviderManager =
|
mDiscoveryProviderManager =
|
||||||
new DiscoveryProviderManager(mContext, mInjector,
|
new DiscoveryProviderManager(mContext, mExecutor, mInjector,
|
||||||
mBleDiscoveryProvider,
|
mBleDiscoveryProvider,
|
||||||
mChreDiscoveryProvider,
|
mChreDiscoveryProvider);
|
||||||
mScanTypeScanListenerRecordMap);
|
|
||||||
mCallerIdentity = CallerIdentity
|
mCallerIdentity = CallerIdentity
|
||||||
.forTest(UID, PID, PACKAGE_NAME, /* attributionTag= */ null);
|
.forTest(UID, PID, PACKAGE_NAME, /* attributionTag= */ null);
|
||||||
}
|
}
|
||||||
|
|
@ -162,55 +158,45 @@ public class DiscoveryProviderManagerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStartProviders_chreOnlyChreAvailable_bleProviderNotStarted() {
|
public void testStartProviders_chreOnlyChreAvailable_bleProviderNotStarted() {
|
||||||
|
reset(mBluetoothController);
|
||||||
when(mChreDiscoveryProvider.available()).thenReturn(true);
|
when(mChreDiscoveryProvider.available()).thenReturn(true);
|
||||||
|
|
||||||
ScanRequest scanRequest = new ScanRequest.Builder()
|
ScanRequest scanRequest = new ScanRequest.Builder()
|
||||||
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
||||||
.addScanFilter(getChreOnlyPresenceScanFilter()).build();
|
.addScanFilter(getChreOnlyPresenceScanFilter()).build();
|
||||||
DiscoveryProviderManager.ScanListenerRecord record =
|
mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
|
||||||
new DiscoveryProviderManager.ScanListenerRecord(
|
|
||||||
scanRequest, mScanListener,
|
|
||||||
mCallerIdentity, mScanListenerDeathRecipient);
|
|
||||||
mScanTypeScanListenerRecordMap.put(mIBinder, record);
|
|
||||||
|
|
||||||
Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
|
Boolean start = mDiscoveryProviderManager.startProviders();
|
||||||
verify(mBluetoothController, never()).start();
|
verify(mBluetoothController, never()).start();
|
||||||
assertThat(start).isTrue();
|
assertThat(start).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStartProviders_chreOnlyChreAvailable_multipleFilters_bleProviderNotStarted() {
|
public void testStartProviders_chreOnlyChreAvailable_multipleFilters_bleProviderNotStarted() {
|
||||||
|
reset(mBluetoothController);
|
||||||
when(mChreDiscoveryProvider.available()).thenReturn(true);
|
when(mChreDiscoveryProvider.available()).thenReturn(true);
|
||||||
|
|
||||||
ScanRequest scanRequest = new ScanRequest.Builder()
|
ScanRequest scanRequest = new ScanRequest.Builder()
|
||||||
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
||||||
.addScanFilter(getChreOnlyPresenceScanFilter())
|
.addScanFilter(getChreOnlyPresenceScanFilter()).build();
|
||||||
.addScanFilter(getPresenceScanFilter()).build();
|
mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
|
||||||
DiscoveryProviderManager.ScanListenerRecord record =
|
|
||||||
new DiscoveryProviderManager.ScanListenerRecord(
|
|
||||||
scanRequest, mScanListener,
|
|
||||||
mCallerIdentity, mScanListenerDeathRecipient);
|
|
||||||
mScanTypeScanListenerRecordMap.put(mIBinder, record);
|
|
||||||
|
|
||||||
Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
|
Boolean start = mDiscoveryProviderManager.startProviders();
|
||||||
verify(mBluetoothController, never()).start();
|
verify(mBluetoothController, never()).start();
|
||||||
assertThat(start).isTrue();
|
assertThat(start).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStartProviders_chreOnlyChreUnavailable_bleProviderNotStarted() {
|
public void testStartProviders_chreOnlyChreUnavailable_bleProviderNotStarted() {
|
||||||
|
reset(mBluetoothController);
|
||||||
when(mChreDiscoveryProvider.available()).thenReturn(false);
|
when(mChreDiscoveryProvider.available()).thenReturn(false);
|
||||||
|
|
||||||
ScanRequest scanRequest = new ScanRequest.Builder()
|
ScanRequest scanRequest = new ScanRequest.Builder()
|
||||||
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
||||||
.addScanFilter(getChreOnlyPresenceScanFilter()).build();
|
.addScanFilter(getChreOnlyPresenceScanFilter()).build();
|
||||||
DiscoveryProviderManager.ScanListenerRecord record =
|
mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
|
||||||
new DiscoveryProviderManager.ScanListenerRecord(
|
|
||||||
scanRequest, mScanListener,
|
|
||||||
mCallerIdentity, mScanListenerDeathRecipient);
|
|
||||||
mScanTypeScanListenerRecordMap.put(mIBinder, record);
|
|
||||||
|
|
||||||
Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
|
Boolean start = mDiscoveryProviderManager.startProviders();
|
||||||
verify(mBluetoothController, never()).start();
|
verify(mBluetoothController, never()).start();
|
||||||
assertThat(start).isFalse();
|
assertThat(start).isFalse();
|
||||||
}
|
}
|
||||||
|
|
@ -218,17 +204,14 @@ public class DiscoveryProviderManagerTest {
|
||||||
@Test
|
@Test
|
||||||
public void testStartProviders_notChreOnlyChreAvailable_bleProviderNotStarted() {
|
public void testStartProviders_notChreOnlyChreAvailable_bleProviderNotStarted() {
|
||||||
when(mChreDiscoveryProvider.available()).thenReturn(true);
|
when(mChreDiscoveryProvider.available()).thenReturn(true);
|
||||||
|
reset(mBluetoothController);
|
||||||
|
|
||||||
ScanRequest scanRequest = new ScanRequest.Builder()
|
ScanRequest scanRequest = new ScanRequest.Builder()
|
||||||
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
||||||
.addScanFilter(getPresenceScanFilter()).build();
|
.addScanFilter(getPresenceScanFilter()).build();
|
||||||
DiscoveryProviderManager.ScanListenerRecord record =
|
mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
|
||||||
new DiscoveryProviderManager.ScanListenerRecord(
|
|
||||||
scanRequest, mScanListener,
|
|
||||||
mCallerIdentity, mScanListenerDeathRecipient);
|
|
||||||
mScanTypeScanListenerRecordMap.put(mIBinder, record);
|
|
||||||
|
|
||||||
Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
|
Boolean start = mDiscoveryProviderManager.startProviders();
|
||||||
verify(mBluetoothController, never()).start();
|
verify(mBluetoothController, never()).start();
|
||||||
assertThat(start).isTrue();
|
assertThat(start).isTrue();
|
||||||
}
|
}
|
||||||
|
|
@ -236,17 +219,14 @@ public class DiscoveryProviderManagerTest {
|
||||||
@Test
|
@Test
|
||||||
public void testStartProviders_notChreOnlyChreUnavailable_bleProviderStarted() {
|
public void testStartProviders_notChreOnlyChreUnavailable_bleProviderStarted() {
|
||||||
when(mChreDiscoveryProvider.available()).thenReturn(false);
|
when(mChreDiscoveryProvider.available()).thenReturn(false);
|
||||||
|
reset(mBluetoothController);
|
||||||
|
|
||||||
ScanRequest scanRequest = new ScanRequest.Builder()
|
ScanRequest scanRequest = new ScanRequest.Builder()
|
||||||
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
||||||
.addScanFilter(getPresenceScanFilter()).build();
|
.addScanFilter(getPresenceScanFilter()).build();
|
||||||
DiscoveryProviderManager.ScanListenerRecord record =
|
mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
|
||||||
new DiscoveryProviderManager.ScanListenerRecord(
|
|
||||||
scanRequest, mScanListener,
|
|
||||||
mCallerIdentity, mScanListenerDeathRecipient);
|
|
||||||
mScanTypeScanListenerRecordMap.put(mIBinder, record);
|
|
||||||
|
|
||||||
Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
|
Boolean start = mDiscoveryProviderManager.startProviders();
|
||||||
verify(mBluetoothController, atLeastOnce()).start();
|
verify(mBluetoothController, atLeastOnce()).start();
|
||||||
assertThat(start).isTrue();
|
assertThat(start).isTrue();
|
||||||
}
|
}
|
||||||
|
|
@ -254,17 +234,14 @@ public class DiscoveryProviderManagerTest {
|
||||||
@Test
|
@Test
|
||||||
public void testStartProviders_chreOnlyChreUndetermined_bleProviderNotStarted() {
|
public void testStartProviders_chreOnlyChreUndetermined_bleProviderNotStarted() {
|
||||||
when(mChreDiscoveryProvider.available()).thenReturn(null);
|
when(mChreDiscoveryProvider.available()).thenReturn(null);
|
||||||
|
reset(mBluetoothController);
|
||||||
|
|
||||||
ScanRequest scanRequest = new ScanRequest.Builder()
|
ScanRequest scanRequest = new ScanRequest.Builder()
|
||||||
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
||||||
.addScanFilter(getChreOnlyPresenceScanFilter()).build();
|
.addScanFilter(getChreOnlyPresenceScanFilter()).build();
|
||||||
DiscoveryProviderManager.ScanListenerRecord record =
|
mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
|
||||||
new DiscoveryProviderManager.ScanListenerRecord(
|
|
||||||
scanRequest, mScanListener,
|
|
||||||
mCallerIdentity, mScanListenerDeathRecipient);
|
|
||||||
mScanTypeScanListenerRecordMap.put(mIBinder, record);
|
|
||||||
|
|
||||||
Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
|
Boolean start = mDiscoveryProviderManager.startProviders();
|
||||||
verify(mBluetoothController, never()).start();
|
verify(mBluetoothController, never()).start();
|
||||||
assertThat(start).isNull();
|
assertThat(start).isNull();
|
||||||
}
|
}
|
||||||
|
|
@ -272,17 +249,14 @@ public class DiscoveryProviderManagerTest {
|
||||||
@Test
|
@Test
|
||||||
public void testStartProviders_notChreOnlyChreUndetermined_bleProviderStarted() {
|
public void testStartProviders_notChreOnlyChreUndetermined_bleProviderStarted() {
|
||||||
when(mChreDiscoveryProvider.available()).thenReturn(null);
|
when(mChreDiscoveryProvider.available()).thenReturn(null);
|
||||||
|
reset(mBluetoothController);
|
||||||
|
|
||||||
ScanRequest scanRequest = new ScanRequest.Builder()
|
ScanRequest scanRequest = new ScanRequest.Builder()
|
||||||
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
||||||
.addScanFilter(getPresenceScanFilter()).build();
|
.addScanFilter(getPresenceScanFilter()).build();
|
||||||
DiscoveryProviderManager.ScanListenerRecord record =
|
mDiscoveryProviderManager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
|
||||||
new DiscoveryProviderManager.ScanListenerRecord(
|
|
||||||
scanRequest, mScanListener,
|
|
||||||
mCallerIdentity, mScanListenerDeathRecipient);
|
|
||||||
mScanTypeScanListenerRecordMap.put(mIBinder, record);
|
|
||||||
|
|
||||||
Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
|
Boolean start = mDiscoveryProviderManager.startProviders();
|
||||||
verify(mBluetoothController, atLeastOnce()).start();
|
verify(mBluetoothController, atLeastOnce()).start();
|
||||||
assertThat(start).isTrue();
|
assertThat(start).isTrue();
|
||||||
}
|
}
|
||||||
|
|
@ -291,22 +265,16 @@ public class DiscoveryProviderManagerTest {
|
||||||
public void test_stopChreProvider_clearFilters() throws Exception {
|
public void test_stopChreProvider_clearFilters() throws Exception {
|
||||||
// Cannot use mocked ChreDiscoveryProvider,
|
// Cannot use mocked ChreDiscoveryProvider,
|
||||||
// so we cannot use class variable mDiscoveryProviderManager
|
// so we cannot use class variable mDiscoveryProviderManager
|
||||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
|
||||||
DiscoveryProviderManager manager =
|
DiscoveryProviderManager manager =
|
||||||
new DiscoveryProviderManager(mContext, mInjector,
|
new DiscoveryProviderManager(mContext, mExecutor, mInjector,
|
||||||
mBleDiscoveryProvider,
|
mBleDiscoveryProvider,
|
||||||
new ChreDiscoveryProvider(
|
new ChreDiscoveryProvider(
|
||||||
mContext,
|
mContext,
|
||||||
new ChreCommunication(mInjector, mContext, executor), executor),
|
new ChreCommunication(mInjector, mContext, mExecutor), mExecutor));
|
||||||
mScanTypeScanListenerRecordMap);
|
|
||||||
ScanRequest scanRequest = new ScanRequest.Builder()
|
ScanRequest scanRequest = new ScanRequest.Builder()
|
||||||
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
||||||
.addScanFilter(getPresenceScanFilter()).build();
|
.addScanFilter(getPresenceScanFilter()).build();
|
||||||
DiscoveryProviderManager.ScanListenerRecord record =
|
manager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
|
||||||
new DiscoveryProviderManager.ScanListenerRecord(
|
|
||||||
scanRequest, mScanListener,
|
|
||||||
mCallerIdentity, mScanListenerDeathRecipient);
|
|
||||||
mScanTypeScanListenerRecordMap.put(mIBinder, record);
|
|
||||||
manager.startChreProvider(List.of(getPresenceScanFilter()));
|
manager.startChreProvider(List.of(getPresenceScanFilter()));
|
||||||
// This is an asynchronized process. The filters will be set in executor thread. So we need
|
// 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.
|
// to wait for some time to get the correct result.
|
||||||
|
|
@ -328,21 +296,17 @@ public class DiscoveryProviderManagerTest {
|
||||||
public void test_restartChreProvider() throws Exception {
|
public void test_restartChreProvider() throws Exception {
|
||||||
// Cannot use mocked ChreDiscoveryProvider,
|
// Cannot use mocked ChreDiscoveryProvider,
|
||||||
// so we cannot use class variable mDiscoveryProviderManager
|
// so we cannot use class variable mDiscoveryProviderManager
|
||||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
|
||||||
DiscoveryProviderManager manager =
|
DiscoveryProviderManager manager =
|
||||||
new DiscoveryProviderManager(mContext, mInjector,
|
new DiscoveryProviderManager(mContext, mExecutor, mInjector,
|
||||||
mBleDiscoveryProvider,
|
mBleDiscoveryProvider,
|
||||||
new ChreDiscoveryProvider(
|
new ChreDiscoveryProvider(
|
||||||
mContext,
|
mContext,
|
||||||
new ChreCommunication(mInjector, mContext, executor), executor),
|
new ChreCommunication(mInjector, mContext, mExecutor), mExecutor));
|
||||||
mScanTypeScanListenerRecordMap);
|
|
||||||
ScanRequest scanRequest = new ScanRequest.Builder()
|
ScanRequest scanRequest = new ScanRequest.Builder()
|
||||||
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
.setScanType(SCAN_TYPE_NEARBY_PRESENCE)
|
||||||
.addScanFilter(getPresenceScanFilter()).build();
|
.addScanFilter(getPresenceScanFilter()).build();
|
||||||
DiscoveryProviderManager.ScanListenerRecord record =
|
manager.registerScanListener(scanRequest, mScanListener, mCallerIdentity);
|
||||||
new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
|
|
||||||
mCallerIdentity, mScanListenerDeathRecipient);
|
|
||||||
mScanTypeScanListenerRecordMap.put(mIBinder, record);
|
|
||||||
manager.startChreProvider(List.of(getPresenceScanFilter()));
|
manager.startChreProvider(List.of(getPresenceScanFilter()));
|
||||||
// This is an asynchronized process. The filters will be set in executor thread. So we need
|
// 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.
|
// to wait for some time to get the correct result.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue