[DO NOT MERGE] Improvements and fixes for BLE scanning within CDM

Test: manual
Bug: 168052577
Change-Id: I16ec39d1139ab6ab0c2c84364493268e8f395d43
(cherry picked from commit 10a1973972)
This commit is contained in:
Eugene Susla
2021-01-12 11:39:43 -08:00
parent 2d29d34fe0
commit 24de1ca1be
2 changed files with 142 additions and 25 deletions

View File

@@ -726,4 +726,43 @@ public interface ServiceConnector<I extends IInterface> {
}
}
}
/**
* A {@link ServiceConnector} that doesn't connect to anything.
*
* @param <T> the type of the {@link IInterface ipc interface} for the remote service
*/
class NoOp<T extends IInterface> extends AndroidFuture<Object> implements ServiceConnector<T> {
{
completeExceptionally(new IllegalStateException("ServiceConnector is a no-op"));
}
@Override
public boolean run(@NonNull VoidJob<T> job) {
return false;
}
@Override
public AndroidFuture<Void> post(@NonNull VoidJob<T> job) {
return (AndroidFuture) this;
}
@Override
public <R> AndroidFuture<R> postForResult(@NonNull Job<T, R> job) {
return (AndroidFuture) this;
}
@Override
public <R> AndroidFuture<R> postAsync(@NonNull Job<T, CompletableFuture<R>> job) {
return (AndroidFuture) this;
}
@Override
public AndroidFuture<T> connect() {
return (AndroidFuture) this;
}
@Override
public void unbind() {}
}
}

View File

@@ -20,8 +20,11 @@ package com.android.server.companion;
import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
import static android.content.Context.BIND_IMPORTANT;
import static android.content.pm.PackageManager.MATCH_ALL;
import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.CollectionUtils.emptyIfNull;
import static com.android.internal.util.CollectionUtils.filter;
import static com.android.internal.util.CollectionUtils.find;
import static com.android.internal.util.CollectionUtils.forEach;
import static com.android.internal.util.CollectionUtils.map;
@@ -45,6 +48,7 @@ import android.app.PendingIntent;
import android.app.role.RoleManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
@@ -58,15 +62,18 @@ import android.companion.ICompanionDeviceDiscoveryService;
import android.companion.ICompanionDeviceManager;
import android.companion.ICompanionDeviceService;
import android.companion.IFindDeviceCallback;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.net.NetworkPolicyManager;
import android.os.Binder;
@@ -186,6 +193,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
new BluetoothDeviceConnectedListener();
private BleStateBroadcastReceiver mBleStateBroadcastReceiver = new BleStateBroadcastReceiver();
private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>();
private UnbindDeviceListenersRunnable
@@ -283,11 +291,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
// Init Bluetooth
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) {
mBluetoothAdapter.registerBluetoothConnectionCallback(
getContext().getMainExecutor(),
mBluetoothDeviceConnectedListener);
getContext().registerReceiver(
mBleStateBroadcastReceiver, mBleStateBroadcastReceiver.mIntentFilter);
initBleScanning();
} else {
Log.w(LOG_TAG, "No BluetoothAdapter available");
@@ -550,7 +561,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
return true;
}
return CollectionUtils.any(
return any(
getAllAssociations(userId, packageName),
a -> Objects.equals(a.getDeviceMacAddress(), macAddress));
}
@@ -902,7 +913,12 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
private List<UserInfo> getAllUsers() {
return getContext().getSystemService(UserManager.class).getUsers();
long identity = Binder.clearCallingIdentity();
try {
return mUserManager.getUsers();
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Nullable
@@ -913,11 +929,16 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
private Set<Association> getAllAssociations() {
ArraySet<Association> result = new ArraySet<>();
for (UserInfo user : mUserManager.getAliveUsers()) {
result.addAll(getAllAssociations(user.id));
long identity = Binder.clearCallingIdentity();
try {
ArraySet<Association> result = new ArraySet<>();
for (UserInfo user : mUserManager.getAliveUsers()) {
result.addAll(getAllAssociations(user.id));
}
return result;
} finally {
Binder.restoreCallingIdentity(identity);
}
return result;
}
private Set<Association> readAllAssociations(int userId) {
@@ -1004,11 +1025,28 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
Association a) {
return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent(
a.getPackageName(),
pkg -> new ServiceConnector.Impl<>(getContext(),
new Intent(CompanionDeviceService.SERVICE_INTERFACE),
BIND_IMPORTANT,
a.getUserId(),
ICompanionDeviceService.Stub::asInterface));
pkg -> createDeviceListenerServiceConnector(a));
}
private ServiceConnector<ICompanionDeviceService> createDeviceListenerServiceConnector(
Association a) {
List<ResolveInfo> resolveInfos = getContext().getPackageManager().queryIntentServicesAsUser(
new Intent(CompanionDeviceService.SERVICE_INTERFACE), MATCH_ALL, a.getUserId());
List<ResolveInfo> packageResolveInfos = filter(resolveInfos,
info -> Objects.equals(info.serviceInfo.packageName, a.getPackageName()));
if (packageResolveInfos.size() != 1) {
Log.w(LOG_TAG, "Device presence listener package must have exactly one "
+ "CompanionDeviceService, but " + a.getPackageName()
+ " has " + packageResolveInfos.size());
return new ServiceConnector.NoOp<>();
}
ComponentName componentName = packageResolveInfos.get(0).serviceInfo.getComponentName();
Log.i(LOG_TAG, "Initializing CompanionDeviceService binding for " + componentName);
return new ServiceConnector.Impl<>(getContext(),
new Intent(CompanionDeviceService.SERVICE_INTERFACE).setComponent(componentName),
BIND_IMPORTANT,
a.getUserId(),
ICompanionDeviceService.Stub::asInterface);
}
private class BleScanCallback extends ScanCallback {
@@ -1034,8 +1072,34 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
if (errorCode == SCAN_FAILED_ALREADY_STARTED) {
// ignore - this might happen if BT tries to auto-restore scans for us in the
// future
Log.i(LOG_TAG, "Ignoring BLE scan error: SCAN_FAILED_ALREADY_STARTED");
} else {
Log.wtf(LOG_TAG, "Failed to start BLE scan: error " + errorCode);
Log.w(LOG_TAG, "Failed to start BLE scan: error " + errorCode);
}
}
}
private class BleStateBroadcastReceiver extends BroadcastReceiver {
final IntentFilter mIntentFilter =
new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
@Override
public void onReceive(Context context, Intent intent) {
int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
Log.i(LOG_TAG, "Received BT state transition broadcast: "
+ BluetoothAdapter.nameForState(previousState)
+ " -> " + BluetoothAdapter.nameForState(newState));
boolean bleOn = newState == BluetoothAdapter.STATE_ON
|| newState == BluetoothAdapter.STATE_BLE_ON;
if (bleOn) {
if (mBluetoothAdapter.getBluetoothLeScanner() != null) {
startBleScan();
} else {
Log.wtf(LOG_TAG, "BLE on, but BluetoothLeScanner == null");
}
}
}
}
@@ -1100,7 +1164,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private void onDeviceNearby(String address) {
Date timestamp = new Date();
mDevicesLastNearby.put(address, timestamp);
Date oldTimestamp = mDevicesLastNearby.put(address, timestamp);
cancelUnbindDeviceListener(address);
@@ -1108,15 +1172,20 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
.computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address))
.schedule();
for (Association association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) {
if (DEBUG) {
Log.i(LOG_TAG, "Device " + address
+ " managed by " + association.getPackageName()
+ " is nearby on " + timestamp);
// Avoid spamming the app if device is already known to be nearby
boolean justAppeared = oldTimestamp == null
|| timestamp.getTime() - oldTimestamp.getTime() >= DEVICE_DISAPPEARED_TIMEOUT_MS;
if (justAppeared) {
for (Association association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) {
if (DEBUG) {
Log.i(LOG_TAG, "Device " + address
+ " managed by " + association.getPackageName()
+ " is nearby on " + timestamp);
}
getDeviceListenerServiceConnector(association).run(
service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
}
getDeviceListenerServiceConnector(association).run(
service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
}
}
}
@@ -1152,6 +1221,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
private void initBleScanning() {
Log.i(LOG_TAG, "initBleScanning()");
boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback(
new BluetoothAdapter.ServiceLifecycleCallback() {
@Override
@@ -1171,14 +1242,21 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
void startBleScan() {
Log.i(LOG_TAG, "startBleScan()");
List<ScanFilter> filters = getBleScanFilters();
if (filters.isEmpty()) {
return;
}
mBluetoothAdapter.getBluetoothLeScanner().startScan(
filters,
new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
mBleScanCallback);
BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
if (scanner == null) {
Log.w(LOG_TAG, "scanner == null (likely BLE isn't ON yet)");
} else {
scanner.startScan(
filters,
new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
mBleScanCallback);
}
}
void restartBleScan() {