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

Commit cc4c46e0 authored by Eugene Susla's avatar Eugene Susla Committed by Android (Google) Code Review
Browse files

Merge "Improvements and fixes for BLE scanning within CDM"

parents 7ea09c10 10a19739
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;
@@ -189,6 +196,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
@@ -286,11 +294,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");
@@ -553,7 +564,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
                return true;
            }

            return CollectionUtils.any(
            return any(
                    getAllAssociations(userId, packageName),
                    a -> Objects.equals(a.getDeviceMacAddress(), macAddress));
        }
@@ -604,6 +615,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
                    return association;
                }
            }));

            restartBleScan();
        }

        private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
@@ -917,7 +930,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);
        }
    }

    private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
@@ -927,11 +945,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);
        }
    }


@@ -1029,11 +1052,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 {
@@ -1059,8 +1099,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");
                }
            }
        }
    }
@@ -1125,7 +1191,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);

@@ -1133,6 +1199,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) {
@@ -1145,6 +1215,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
                }
            }
        }
    }

    private void onDeviceDisappeared(String address) {
        boolean hasDeviceListeners = false;
@@ -1177,6 +1248,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
    }

    private void initBleScanning() {
        Log.i(LOG_TAG, "initBleScanning()");

        boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback(
                new BluetoothAdapter.ServiceLifecycleCallback() {
                    @Override
@@ -1196,15 +1269,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);