Merge "[DO NOT MERGE] Improvements and fixes for BLE scanning within CDM" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-02-09 02:25:06 +00:00
committed by Android (Google) Code Review
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.CALLBACK_TYPE_ALL_MATCHES;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED; import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
import static android.content.Context.BIND_IMPORTANT; 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.emptyIfNull;
import static com.android.internal.util.CollectionUtils.filter;
import static com.android.internal.util.CollectionUtils.find; import static com.android.internal.util.CollectionUtils.find;
import static com.android.internal.util.CollectionUtils.forEach; import static com.android.internal.util.CollectionUtils.forEach;
import static com.android.internal.util.CollectionUtils.map; import static com.android.internal.util.CollectionUtils.map;
@@ -45,6 +48,7 @@ import android.app.PendingIntent;
import android.app.role.RoleManager; import android.app.role.RoleManager;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanResult;
@@ -58,15 +62,18 @@ import android.companion.ICompanionDeviceDiscoveryService;
import android.companion.ICompanionDeviceManager; import android.companion.ICompanionDeviceManager;
import android.companion.ICompanionDeviceService; import android.companion.ICompanionDeviceService;
import android.companion.IFindDeviceCallback; import android.companion.IFindDeviceCallback;
import android.content.BroadcastReceiver;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.FeatureInfo; import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo; import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.net.NetworkPolicyManager; import android.net.NetworkPolicyManager;
import android.os.Binder; import android.os.Binder;
@@ -186,6 +193,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener = private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
new BluetoothDeviceConnectedListener(); new BluetoothDeviceConnectedListener();
private BleStateBroadcastReceiver mBleStateBroadcastReceiver = new BleStateBroadcastReceiver();
private List<String> mCurrentlyConnectedDevices = new ArrayList<>(); private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>(); private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>();
private UnbindDeviceListenersRunnable private UnbindDeviceListenersRunnable
@@ -283,11 +291,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
@Override @Override
public void onBootPhase(int phase) { public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
// Init Bluetooth
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) { if (mBluetoothAdapter != null) {
mBluetoothAdapter.registerBluetoothConnectionCallback( mBluetoothAdapter.registerBluetoothConnectionCallback(
getContext().getMainExecutor(), getContext().getMainExecutor(),
mBluetoothDeviceConnectedListener); mBluetoothDeviceConnectedListener);
getContext().registerReceiver(
mBleStateBroadcastReceiver, mBleStateBroadcastReceiver.mIntentFilter);
initBleScanning(); initBleScanning();
} else { } else {
Log.w(LOG_TAG, "No BluetoothAdapter available"); Log.w(LOG_TAG, "No BluetoothAdapter available");
@@ -550,7 +561,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
return true; return true;
} }
return CollectionUtils.any( return any(
getAllAssociations(userId, packageName), getAllAssociations(userId, packageName),
a -> Objects.equals(a.getDeviceMacAddress(), macAddress)); a -> Objects.equals(a.getDeviceMacAddress(), macAddress));
} }
@@ -902,7 +913,12 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
} }
private List<UserInfo> getAllUsers() { private List<UserInfo> getAllUsers() {
return getContext().getSystemService(UserManager.class).getUsers(); long identity = Binder.clearCallingIdentity();
try {
return mUserManager.getUsers();
} finally {
Binder.restoreCallingIdentity(identity);
}
} }
@Nullable @Nullable
@@ -913,11 +929,16 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
} }
private Set<Association> getAllAssociations() { private Set<Association> getAllAssociations() {
long identity = Binder.clearCallingIdentity();
try {
ArraySet<Association> result = new ArraySet<>(); ArraySet<Association> result = new ArraySet<>();
for (UserInfo user : mUserManager.getAliveUsers()) { for (UserInfo user : mUserManager.getAliveUsers()) {
result.addAll(getAllAssociations(user.id)); result.addAll(getAllAssociations(user.id));
} }
return result; return result;
} finally {
Binder.restoreCallingIdentity(identity);
}
} }
private Set<Association> readAllAssociations(int userId) { private Set<Association> readAllAssociations(int userId) {
@@ -1004,11 +1025,28 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
Association a) { Association a) {
return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent( return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent(
a.getPackageName(), a.getPackageName(),
pkg -> new ServiceConnector.Impl<>(getContext(), pkg -> createDeviceListenerServiceConnector(a));
new Intent(CompanionDeviceService.SERVICE_INTERFACE), }
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, BIND_IMPORTANT,
a.getUserId(), a.getUserId(),
ICompanionDeviceService.Stub::asInterface)); ICompanionDeviceService.Stub::asInterface);
} }
private class BleScanCallback extends ScanCallback { private class BleScanCallback extends ScanCallback {
@@ -1034,8 +1072,34 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
if (errorCode == SCAN_FAILED_ALREADY_STARTED) { if (errorCode == SCAN_FAILED_ALREADY_STARTED) {
// ignore - this might happen if BT tries to auto-restore scans for us in the // ignore - this might happen if BT tries to auto-restore scans for us in the
// future // future
Log.i(LOG_TAG, "Ignoring BLE scan error: SCAN_FAILED_ALREADY_STARTED");
} else { } 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) { private void onDeviceNearby(String address) {
Date timestamp = new Date(); Date timestamp = new Date();
mDevicesLastNearby.put(address, timestamp); Date oldTimestamp = mDevicesLastNearby.put(address, timestamp);
cancelUnbindDeviceListener(address); cancelUnbindDeviceListener(address);
@@ -1108,6 +1172,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
.computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address)) .computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address))
.schedule(); .schedule();
// 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)) { for (Association association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) { if (association.isNotifyOnDeviceNearby()) {
if (DEBUG) { if (DEBUG) {
@@ -1120,6 +1188,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
} }
} }
} }
}
private void onDeviceDisappeared(String address) { private void onDeviceDisappeared(String address) {
boolean hasDeviceListeners = false; boolean hasDeviceListeners = false;
@@ -1152,6 +1221,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
} }
private void initBleScanning() { private void initBleScanning() {
Log.i(LOG_TAG, "initBleScanning()");
boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback( boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback(
new BluetoothAdapter.ServiceLifecycleCallback() { new BluetoothAdapter.ServiceLifecycleCallback() {
@Override @Override
@@ -1171,15 +1242,22 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
} }
void startBleScan() { void startBleScan() {
Log.i(LOG_TAG, "startBleScan()");
List<ScanFilter> filters = getBleScanFilters(); List<ScanFilter> filters = getBleScanFilters();
if (filters.isEmpty()) { if (filters.isEmpty()) {
return; return;
} }
mBluetoothAdapter.getBluetoothLeScanner().startScan( BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
if (scanner == null) {
Log.w(LOG_TAG, "scanner == null (likely BLE isn't ON yet)");
} else {
scanner.startScan(
filters, filters,
new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(), new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
mBleScanCallback); mBleScanCallback);
} }
}
void restartBleScan() { void restartBleScan() {
mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback); mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback);