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

Commit cc32d2d2 authored by William Escande's avatar William Escande
Browse files

LeAudio: Do not use framework to start/stop scan

When a 3p app is calling Bluetooth framework to register a scanner,
there is an IPC call being made to the Bluetooth process, that will
register it and then perform a callback (another IPC) to the 3p app.

Binder has an optimization in which, if both side of the binder are in
the same process, there is no IPC being done and this is a direct call.

Here, LeAudioService is inside the Bluetooth app, and it is calling the
Bluetooth framework.

The issue is that the scanner is registered synchronously, the callback
is called synchronously and the notifyAll is perform before the code
start to wait. So it notify nobodies.

Theoretically, the wait can also be 2 seconds long for 3p app, if the
thread is very unlucky. And this design should be address entirely.

Bug: 348562830
Bug: 340740517
Bug: 347880944
Test: None
Flag: com.android.bluetooth.flags.leaudio_call_start_scan_directly
Change-Id: I89e409521dbca54a1c6fb7b1a968c95d247d8e8f
parent bfcf8aab
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -5963,10 +5963,18 @@ public class AdapterService extends Service {
        return mGattService == null ? null : mGattService.getBinder();
    }

    public GattService getBluetoothGattService() {
        return mGattService;
    }

    IBinder getBluetoothScan() {
        return mScanController == null ? null : mScanController.getBinder();
    }

    public ScanController getBluetoothScanController() {
        return mScanController;
    }

    void unregAllGattClient(AttributionSource source) {
        if (mGattService != null) {
            mGattService.unregAll(source);
+1 −1
Original line number Diff line number Diff line
@@ -292,7 +292,7 @@ public class GattService extends ProfileService {
        sGattService = instance;
    }

    TransitionalScanHelper getTransitionalScanHelper() {
    public TransitionalScanHelper getTransitionalScanHelper() {
        return mTransitionalScanHelper;
    }

+75 −2
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import android.bluetooth.IBluetoothLeAudioCallback;
import android.bluetooth.IBluetoothLeBroadcastCallback;
import android.bluetooth.IBluetoothVolumeControl;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.IScannerCallback;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
@@ -222,6 +223,8 @@ public class LeAudioService extends ProfileService {
    /* When mScanCallback is not null, it means scan is started. */
    ScanCallback mScanCallback;

    private final AudioServerScanCallback2 mScanCallback2 = new AudioServerScanCallback2();

    public LeAudioService(Context ctx) {
        this(ctx, LeAudioNativeInterface.getInstance());
    }
@@ -1790,8 +1793,68 @@ public class LeAudioService extends ProfileService {
        return true;
    }

    private class AudioServerScanCallback2 extends IScannerCallback.Stub {
        // See BluetoothLeScanner.BleScanCallbackWrapper.mScannerId
        int mScannerId = 0;

        synchronized void startBackgroundScan() {
            if (mScannerId != 0) {
                Log.d(TAG, "Scanner is already registered with id " + mScannerId);
                return;
            }
            if (Flags.scanManagerRefactor()) {
                mAdapterService
                        .getBluetoothScanController()
                        .getTransitionalScanHelper()
                        .registerScannerInternal(this, null);
            } else {
                mAdapterService
                        .getBluetoothGattService()
                        .getTransitionalScanHelper()
                        .registerScannerInternal(this, null);
            }
        }

        synchronized void stopBackgroundScan() {
            if (mScannerId == 0) {
                Log.d(TAG, "Scanner is already unregistered");
                return;
            }
            if (Flags.scanManagerRefactor()) {
                mAdapterService
                        .getBluetoothScanController()
                        .getTransitionalScanHelper()
                        .unregisterScannerInternal(mScannerId);
            } else {
                mAdapterService
                        .getBluetoothGattService()
                        .getTransitionalScanHelper()
                        .unregisterScannerInternal(mScannerId);
            }
        }

        @Override
        public synchronized void onScannerRegistered(int status, int scannerId) {
            mScannerId = scannerId;
        }

        // Eventually we should be able to start scan from native when b/276350722 is done
        // All the result returned here are ignored
        @Override
        public void onScanResult(ScanResult scanResult) {}

        @Override
        public void onBatchScanResults(List<ScanResult> batchResults) {}

        @Override
        public void onFoundOrLost(boolean onFound, ScanResult scanResult) {}

        @Override
        public void onScanManagerErrorCallback(int errorCode) {}
    }

    private class AudioServerScanCallback extends ScanCallback {
        int mMaxScanRetires = 10;
        int mMaxScanRetries = 10;
        int mScanRetries = 0;

        @Override
@@ -1814,7 +1877,7 @@ public class LeAudioService extends ProfileService {
            switch (errorCode) {
                case SCAN_FAILED_INTERNAL_ERROR:
                case SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
                    if (mScanRetries < mMaxScanRetires) {
                    if (mScanRetries < mMaxScanRetries) {
                        mScanRetries++;
                        Log.w(TAG, "Failed to start. Let's retry");
                        mHandler.post(() -> startAudioServersBackgroundScan(/* retry= */ true));
@@ -2793,6 +2856,11 @@ public class LeAudioService extends ProfileService {
    void stopAudioServersBackgroundScan() {
        Log.d(TAG, "stopAudioServersBackgroundScan");

        if (Flags.leaudioCallStartScanDirectly()) {
            mScanCallback2.stopBackgroundScan();
            return;
        }

        if (mAudioServersScanner == null || mScanCallback == null) {
            Log.d(TAG, "stopAudioServersBackgroundScan: already stopped");
            return;
@@ -2815,6 +2883,11 @@ public class LeAudioService extends ProfileService {
            return;
        }

        if (Flags.leaudioCallStartScanDirectly()) {
            mScanCallback2.startBackgroundScan();
            return;
        }

        if (mAudioServersScanner == null) {
            mAudioServersScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();
            if (mAudioServersScanner == null) {
+1 −1
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ public class ScanController {
        mTransitionalScanHelper.notifyProfileConnectionStateChange(profile, fromState, toState);
    }

    TransitionalScanHelper getTransitionalScanHelper() {
    public TransitionalScanHelper getTransitionalScanHelper() {
        return mTransitionalScanHelper;
    }

+12 −3
Original line number Diff line number Diff line
@@ -1065,9 +1065,6 @@ public class TransitionalScanHelper {
            return;
        }

        UUID uuid = UUID.randomUUID();
        Log.d(TAG, "registerScanner() - UUID=" + uuid);

        enforceImpersonatationPermissionIfNeeded(workSource);

        AppScanStats app = mScannerMap.getAppScanStatsByUid(Binder.getCallingUid());
@@ -1082,6 +1079,13 @@ public class TransitionalScanHelper {
            }
            return;
        }
        registerScannerInternal(callback, workSource);
    }

    /** Intended for internal use within the Bluetooth app. Bypass permission check */
    public void registerScannerInternal(IScannerCallback callback, WorkSource workSource) {
        UUID uuid = UUID.randomUUID();
        Log.d(TAG, "registerScanner() - UUID=" + uuid);

        mScannerMap.add(uuid, workSource, callback, null, mContext, this);
        mScanManager.registerScanner(uuid);
@@ -1094,6 +1098,11 @@ public class TransitionalScanHelper {
            return;
        }

        unregisterScannerInternal(scannerId);
    }

    /** Intended for internal use within the Bluetooth app. Bypass permission check */
    public void unregisterScannerInternal(int scannerId) {
        Log.d(TAG, "unregisterScanner() - scannerId=" + scannerId);
        mScannerMap.remove(scannerId);
        mScanManager.unregisterScanner(scannerId);