Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10a19739 authored by Eugene Susla's avatar Eugene Susla
Browse files

Improvements and fixes for BLE scanning within CDM

Test: manual
Bug: 168052577
Change-Id: I16ec39d1139ab6ab0c2c84364493268e8f395d43
parent 585f7086
Loading
Loading
Loading
Loading
+39 −0
Original line number Diff line number Diff line
@@ -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() {}
    }
}
+105 −25
Original line number Diff line number Diff line
@@ -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));
        }
@@ -600,6 +611,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
                    return association;
                }
            }));

            restartBleScan();
        }

        private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
@@ -902,7 +915,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 +931,16 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
    }

    private Set<Association> getAllAssociations() {
        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);
        }
    }

    private Set<Association> readAllAssociations(int userId) {
@@ -1004,11 +1027,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),
                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));
                ICompanionDeviceService.Stub::asInterface);
    }

    private class BleScanCallback extends ScanCallback {
@@ -1034,8 +1074,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 +1166,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,6 +1174,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
                .computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address))
                .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)) {
                if (association.isNotifyOnDeviceNearby()) {
                    if (DEBUG) {
@@ -1120,6 +1190,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
                }
            }
        }
    }

    private void onDeviceDisappeared(String address) {
        boolean hasDeviceListeners = false;
@@ -1152,6 +1223,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,15 +1244,22 @@ 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(
        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() {
        mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback);