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

Commit 45f5f910 authored by Michal Belusiak (xWF)'s avatar Michal Belusiak (xWF) Committed by Gerrit Code Review
Browse files

Merge "BassClientService: Sort scan results for source sync by fails counter" into main

parents 2735e4a7 2be3cada
Loading
Loading
Loading
Loading
+54 −33
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeriphe
import static com.android.bluetooth.flags.Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastMonitorSourceSyncStatus;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastResyncHelper;
import static com.android.bluetooth.flags.Flags.leaudioSortScansToSyncByFails;

import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
@@ -158,6 +159,7 @@ public class BassClientService extends ProfileService {
            new HashMap<>();
    private final PriorityQueue<SourceSyncRequest> mSourceSyncRequestsQueue =
            new PriorityQueue<>(sSourceSyncRequestComparator);
    private final Map<Integer, Integer> mSyncFailureCounter = new HashMap<>();
    private final Map<Integer, Integer> mBisDiscoveryCounterMap = new HashMap<>();
    private final List<AddSourceData> mPendingSourcesToAdd = new ArrayList<>();

@@ -280,12 +282,14 @@ public class BassClientService extends ProfileService {
    }

    private static class SourceSyncRequest {
        private ScanResult mScanResult;
        private boolean mHasPriority;
        private final ScanResult mScanResult;
        private final boolean mHasPriority;
        private final int mSyncFailureCounter;

        SourceSyncRequest(ScanResult scanResult, boolean hasPriority) {
        SourceSyncRequest(ScanResult scanResult, boolean hasPriority, int syncFailureCounter) {
            this.mScanResult = scanResult;
            this.mHasPriority = hasPriority;
            this.mSyncFailureCounter = syncFailureCounter;
        }

        public ScanResult getScanResult() {
@@ -300,6 +304,10 @@ public class BassClientService extends ProfileService {
            return mHasPriority;
        }

        public int getFailsCounter() {
            return mSyncFailureCounter;
        }

        @Override
        public String toString() {
            return "SourceSyncRequest{"
@@ -307,6 +315,8 @@ public class BassClientService extends ProfileService {
                    + mScanResult
                    + ", mHasPriority="
                    + mHasPriority
                    + ", mSyncFailureCounter="
                    + mSyncFailureCounter
                    + '}';
        }
    }
@@ -319,6 +329,9 @@ public class BassClientService extends ProfileService {
                        return -1;
                    } else if (!ssr1.hasPriority() && ssr2.hasPriority()) {
                        return 1;
                    } else if (leaudioSortScansToSyncByFails()
                            && (ssr1.getFailsCounter() != ssr2.getFailsCounter())) {
                        return Integer.compare(ssr1.getFailsCounter(), ssr2.getFailsCounter());
                    } else {
                        return Integer.compare(ssr2.getRssi(), ssr1.getRssi());
                    }
@@ -1046,9 +1059,8 @@ public class BassClientService extends ProfileService {
            if (isPlaying) {
                stopBigMonitoring(broadcastId, false);
            } else if (!mPausedBroadcastIds.containsKey(broadcastId)) {
                ScanResult scanRes = getCachedBroadcast(broadcastId);
                if (scanRes != null) {
                    addSelectSourceRequest(scanRes, true);
                if (mCachedBroadcasts.containsKey(broadcastId)) {
                    addSelectSourceRequest(broadcastId, true);
                    mPausedBroadcastIds.put(broadcastId, PauseType.SINK_UNKNOWN);
                    logPausedBroadcastsAndSinks();
                    mHandler.removeMessages(MESSAGE_BIG_CHECK_START);
@@ -1842,11 +1854,11 @@ public class BassClientService extends ProfileService {
                                        "Broadcast Source Found: Broadcast ID: " + broadcastId);

                                if (broadcastId != BassConstants.INVALID_BROADCAST_ID
                                        && mCachedBroadcasts.get(broadcastId) == null) {
                                        && !mCachedBroadcasts.containsKey(broadcastId)) {
                                    log("selectBroadcastSource: broadcastId " + broadcastId);
                                    mCachedBroadcasts.put(broadcastId, result);
                                    if (leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
                                        addSelectSourceRequest(result, false);
                                        addSelectSourceRequest(broadcastId, false);
                                    } else {
                                        synchronized (mStateMachines) {
                                            for (BassClientStateMachine sm :
@@ -1866,15 +1878,13 @@ public class BassClientService extends ProfileService {
                            informConnectedDeviceAboutScanOffloadStop();
                        }
                    };
            mSyncFailureCounter.clear();
            mHandler.removeMessages(MESSAGE_SYNC_TIMEOUT);
            if (leaudioBroadcastResyncHelper()) {
                // Sync to the broadcasts already synced with sinks
                Set<Integer> syncedBroadcasts = getExternalBroadcastsActiveOnSinks();
                for (int syncedBroadcast : syncedBroadcasts) {
                    ScanResult scanRes = getCachedBroadcast(syncedBroadcast);
                    if (scanRes != null) {
                        addSelectSourceRequest(scanRes, true);
                    }
                    addSelectSourceRequest(syncedBroadcast, true);
                }
            }
            // when starting scan, clear the previously cached broadcast scan results
@@ -1972,10 +1982,7 @@ public class BassClientService extends ProfileService {
                    Integer broadcastId = entry.getKey();
                    PauseType pauseType = entry.getValue();
                    if (pauseType != PauseType.HOST_INTENTIONAL) {
                        ScanResult scanRes = getCachedBroadcast(broadcastId);
                        if (scanRes != null) {
                            addSelectSourceRequest(scanRes, true);
                        }
                        addSelectSourceRequest(broadcastId, true);
                    }
                }
            }
@@ -1985,6 +1992,7 @@ public class BassClientService extends ProfileService {
    private void clearAllSyncData() {
        log("clearAllSyncData");
        mSourceSyncRequestsQueue.clear();
        mSyncFailureCounter.clear();
        mPendingSourcesToAdd.clear();

        cancelActiveSync(null);
@@ -2106,6 +2114,10 @@ public class BassClientService extends ProfileService {
                    }
                }
                stopBigMonitoring(broadcastId, false);
                synchronized (mSourceSyncRequestsQueue) {
                    int failsCounter = mSyncFailureCounter.getOrDefault(broadcastId, 0) + 1;
                    mSyncFailureCounter.put(broadcastId, failsCounter);
                }
                synchronized (mSearchScanCallbackLock) {
                    // Clear from cache to make possible sync again (only during active searching)
                    if (mSearchScanCallback != null) {
@@ -2180,6 +2192,10 @@ public class BassClientService extends ProfileService {
                    mCallbacks.notifySourceLost(broadcastId);
                }
                stopBigMonitoring(broadcastId, false);
                synchronized (mSourceSyncRequestsQueue) {
                    int failsCounter = mSyncFailureCounter.getOrDefault(broadcastId, 0) + 1;
                    mSyncFailureCounter.put(broadcastId, failsCounter);
                }
            }
            clearAllDataForSyncHandle(syncHandle);
            // Clear from cache to make possible sync again (only during active searching)
@@ -2464,19 +2480,16 @@ public class BassClientService extends ProfileService {
        return broadcastName;
    }

    void addSelectSourceRequest(ScanResult scanRes, boolean hasPriority) {
    void addSelectSourceRequest(int broadcastId, boolean hasPriority) {
        sEventLogger.logd(
                TAG,
                "Add Select Broadcast Source, result: "
                        + scanRes
                "Add Select Broadcast Source, broadcastId: "
                        + broadcastId
                        + ", hasPriority: "
                        + hasPriority);

        if (scanRes == null) {
            Log.e(TAG, "addSelectSourceRequest: Error bad parameters: scanRes cannot be null");
            return;
        }

        ScanResult scanRes = getCachedBroadcast(broadcastId);
        if (scanRes != null) {
            ScanRecord scanRecord = scanRes.getScanRecord();
            if (scanRecord == null) {
                log("addSelectSourceRequest: ScanRecord empty");
@@ -2484,10 +2497,18 @@ public class BassClientService extends ProfileService {
            }

            synchronized (mSourceSyncRequestsQueue) {
            mSourceSyncRequestsQueue.add(new SourceSyncRequest(scanRes, hasPriority));
                if (!mSyncFailureCounter.containsKey(broadcastId)) {
                    mSyncFailureCounter.put(broadcastId, 0);
                }
                mSourceSyncRequestsQueue.add(
                        new SourceSyncRequest(
                                scanRes, hasPriority, mSyncFailureCounter.get(broadcastId)));
            }

            handleSelectSourceRequest();
        } else {
            log("addSelectSourceRequest: ScanResult empty");
        }
    }

    @SuppressLint("AndroidFrameworkRequiresPermission") // TODO: b/350563786 - Fix BASS annotation
@@ -2732,7 +2753,7 @@ public class BassClientService extends ProfileService {
                        mPendingSourcesToAdd.add(
                                new AddSourceData(sink, sourceMetadata, isGroupOp));
                        if (!alreadyAdded) {
                            addSelectSourceRequest(getCachedBroadcast(broadcastId), true);
                            addSelectSourceRequest(broadcastId, true);
                        }
                    }
                } else {
+255 −42
Original line number Diff line number Diff line
@@ -2377,35 +2377,11 @@ public class BassClientServiceTest {
    @Test
    @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE)
    public void testSelectSource_withSameBroadcastId() {
        byte[] scanRecord = getScanRecord(TEST_BROADCAST_ID);

        ScanResult scanResult1 =
                new ScanResult(
                        mSourceDevice,
                        0,
                        0,
                        0,
                        0,
                        0,
                        TEST_RSSI,
                        0,
                        ScanRecord.parseFromBytes(scanRecord),
                        0);
        ScanResult scanResult2 =
                new ScanResult(
                        mSourceDevice2,
                        0,
                        0,
                        0,
                        0,
                        0,
                        TEST_RSSI,
                        0,
                        ScanRecord.parseFromBytes(scanRecord),
                        0);
        prepareConnectedDeviceGroup();
        startSearchingForSources();

        // First selectSource
        mBassClientService.addSelectSourceRequest(scanResult1, false);
        onScanResult(mSourceDevice, TEST_BROADCAST_ID);
        mInOrderMethodProxy
                .verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
@@ -2414,7 +2390,7 @@ public class BassClientServiceTest {
        onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);

        // Second selectSource with the same broadcast id
        mBassClientService.addSelectSourceRequest(scanResult2, false);
        onScanResult(mSourceDevice2, TEST_BROADCAST_ID);
        mInOrderMethodProxy
                .verify(mMethodProxy, never())
                .periodicAdvertisingManagerRegisterSync(
@@ -2491,7 +2467,9 @@ public class BassClientServiceTest {
                        ScanRecord.parseFromBytes(scanRecord),
                        0);

        mBassClientService.addSelectSourceRequest(scanResult, false);
        prepareConnectedDeviceGroup();
        startSearchingForSources();
        mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
        verify(mMethodProxy, never())
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
@@ -2591,7 +2569,9 @@ public class BassClientServiceTest {
                        ScanRecord.parseFromBytes(scanRecord),
                        0);

        mBassClientService.addSelectSourceRequest(scanResult, false);
        prepareConnectedDeviceGroup();
        startSearchingForSources();
        mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
        verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
@@ -2667,7 +2647,9 @@ public class BassClientServiceTest {
                        ScanRecord.parseFromBytes(scanRecord),
                        0);

        mBassClientService.addSelectSourceRequest(scanResult, false);
        prepareConnectedDeviceGroup();
        startSearchingForSources();
        mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
        verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
@@ -3318,7 +3300,7 @@ public class BassClientServiceTest {

    @Test
    @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE)
    public void testSelectSource_orderOfSyncRegistering() {
    public void testSelectSource_orderOfSyncRegisteringByPriorityAndRssi() {
        final BluetoothDevice device1 =
                mBluetoothAdapter.getRemoteLeDevice(
                        "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
@@ -3441,20 +3423,42 @@ public class BassClientServiceTest {
                        ScanRecord.parseFromBytes(scanRecord7),
                        0);

        prepareConnectedDeviceGroup();
        startSearchingForSources();

        // Added and executed immidiatelly as no other in queue
        mBassClientService.addSelectSourceRequest(scanResult1, false);
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1);
        // Added to queue with worst rssi
        mBassClientService.addSelectSourceRequest(scanResult2, false);
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2);
        // Added to queue with best rssi
        mBassClientService.addSelectSourceRequest(scanResult3, false);
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3);
        // Added to queue with medium rssi
        mBassClientService.addSelectSourceRequest(scanResult4, false);
        // Added to queue with priority and worst rssi
        mBassClientService.addSelectSourceRequest(scanResult5, true);
        // Added to queue with priority and best rssi
        mBassClientService.addSelectSourceRequest(scanResult6, true);
        // Added to queue with priority and medium rssi
        mBassClientService.addSelectSourceRequest(scanResult7, true);
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult4);
        // Added to queue with worst rssi (increase priority after all)
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult5);
        // Added to queue with best rssi (increase priority after all)
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult6);
        // Added to queue with medium rssi (increase priority after all)
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult7);

        // Increase priority of last 3 of them
        mBassClientService.addSelectSourceRequest(broadcastId5, true);
        mBassClientService.addSelectSourceRequest(broadcastId6, true);
        mBassClientService.addSelectSourceRequest(broadcastId7, true);

        ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class);
        mInOrderMethodProxy
@@ -3555,6 +3559,215 @@ public class BassClientServiceTest {
                .isEqualTo(broadcastId2);
    }

    @Test
    @EnableFlags({
        Flags.FLAG_LEAUDIO_SORT_SCANS_TO_SYNC_BY_FAILS,
        Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE
    })
    public void testSelectSource_orderOfSyncRegisteringByRssiAndFailsCounter() {
        final BluetoothDevice device1 =
                mBluetoothAdapter.getRemoteLeDevice(
                        "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
        final BluetoothDevice device2 =
                mBluetoothAdapter.getRemoteLeDevice(
                        "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM);
        final BluetoothDevice device3 =
                mBluetoothAdapter.getRemoteLeDevice(
                        "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM);
        final int broadcastId1 = 1111;
        final int broadcastId2 = 2222;
        final int broadcastId3 = 3333;

        byte[] scanRecord1 = getScanRecord(broadcastId1);
        byte[] scanRecord2 = getScanRecord(broadcastId2);
        byte[] scanRecord3 = getScanRecord(broadcastId3);

        ScanResult scanResult1 =
                new ScanResult(
                        device1,
                        0,
                        0,
                        0,
                        0,
                        0,
                        TEST_RSSI + 10,
                        0,
                        ScanRecord.parseFromBytes(scanRecord1),
                        0);
        ScanResult scanResult2 =
                new ScanResult(
                        device2,
                        0,
                        0,
                        0,
                        0,
                        0,
                        TEST_RSSI + 9,
                        0,
                        ScanRecord.parseFromBytes(scanRecord2),
                        0);
        ScanResult scanResult3 =
                new ScanResult(
                        device3,
                        0,
                        0,
                        0,
                        0,
                        0,
                        TEST_RSSI,
                        0,
                        ScanRecord.parseFromBytes(scanRecord3),
                        0);

        prepareConnectedDeviceGroup();
        startSearchingForSources();

        // Test using onSyncEstablishedFailed

        // Added and executed immidiatelly as no other in queue, high rssi
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1);
        // Added to queue, medium rssi
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2);
        // Added to queue, low rssi
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3);

        ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class);
        mInOrderMethodProxy
                .verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
        assertThat(
                        BassUtils.parseBroadcastId(
                                resultCaptor
                                        .getValue()
                                        .getScanRecord()
                                        .getServiceData()
                                        .get(BassConstants.BAAS_UUID)))
                .isEqualTo(broadcastId1);

        onSyncEstablishedFailed(device1, TEST_SYNC_HANDLE);
        mInOrderMethodProxy
                .verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
        assertThat(
                        BassUtils.parseBroadcastId(
                                resultCaptor
                                        .getValue()
                                        .getScanRecord()
                                        .getServiceData()
                                        .get(BassConstants.BAAS_UUID)))
                .isEqualTo(broadcastId2);

        // Added to queue again, high rssi
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1);

        onSyncEstablishedFailed(device2, TEST_SYNC_HANDLE + 1);
        mInOrderMethodProxy
                .verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
        assertThat(
                        BassUtils.parseBroadcastId(
                                resultCaptor
                                        .getValue()
                                        .getScanRecord()
                                        .getServiceData()
                                        .get(BassConstants.BAAS_UUID)))
                .isEqualTo(broadcastId3);

        onSyncEstablished(device3, TEST_SYNC_HANDLE + 2);
        mInOrderMethodProxy
                .verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
        assertThat(
                        BassUtils.parseBroadcastId(
                                resultCaptor
                                        .getValue()
                                        .getScanRecord()
                                        .getServiceData()
                                        .get(BassConstants.BAAS_UUID)))
                .isEqualTo(broadcastId1);

        // Restart searching clears the mSyncFailureCounter
        mBassClientService.stopSearchingForSources();
        mInOrderMethodProxy
                .verify(mMethodProxy, times(2))
                .periodicAdvertisingManagerUnregisterSync(any(), any());
        startSearchingForSources();

        // Test using onSyncLost

        // Added and executed immidiatelly as no other in queue, high rssi
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1);
        // Added to queue, medium rssi
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2);
        // Added to queue, low rssi
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3);

        mInOrderMethodProxy
                .verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
        assertThat(
                        BassUtils.parseBroadcastId(
                                resultCaptor
                                        .getValue()
                                        .getScanRecord()
                                        .getServiceData()
                                        .get(BassConstants.BAAS_UUID)))
                .isEqualTo(broadcastId1);

        onSyncEstablished(device1, TEST_SYNC_HANDLE);
        mInOrderMethodProxy
                .verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
        assertThat(
                        BassUtils.parseBroadcastId(
                                resultCaptor
                                        .getValue()
                                        .getScanRecord()
                                        .getServiceData()
                                        .get(BassConstants.BAAS_UUID)))
                .isEqualTo(broadcastId2);
        onSyncLost();

        // Added to queue again, high rssi
        mCallbackCaptor
                .getValue()
                .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1);

        onSyncEstablished(device2, TEST_SYNC_HANDLE + 1);
        mInOrderMethodProxy
                .verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
        assertThat(
                        BassUtils.parseBroadcastId(
                                resultCaptor
                                        .getValue()
                                        .getScanRecord()
                                        .getServiceData()
                                        .get(BassConstants.BAAS_UUID)))
                .isEqualTo(broadcastId3);
    }

    @Test
    @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE)
    public void testSelectSource_invalidActiveSource() {