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

Commit e3de0cd5 authored by Michal Belusiak's avatar Michal Belusiak Committed by Michal Belusiak (xWF)
Browse files

BassClientService: Execute one selectSource for all sinks for the same broadcaster

If pair of buds try to add source of inactive broadcaster, execute only
one selectSource for it.

Bug: 354890418
Test: atest BassClientServiceTest
Flag: Exempt, trivial fix covered with unit tests
Change-Id: Ic1b35a3542b1f2a9ec6cd7abe95c1bea48f5acdc
parent 6096680d
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -2560,11 +2560,21 @@ public class BassClientService extends ProfileService {
                if (broadcastId != BassConstants.INVALID_BROADCAST_ID
                        && getCachedBroadcast(broadcastId) != null) {
                    // If the source has been synced before, try to re-sync
                    // with the source by previously cached scan result
                    addSelectSourceRequest(getCachedBroadcast(broadcastId), true);
                    // with the source by previously cached scan result.
                    // Check if not added already
                    boolean alreadyAdded = false;
                    synchronized (mPendingSourcesToAdd) {
                        for (AddSourceData pendingSourcesToAdd : mPendingSourcesToAdd) {
                            if (pendingSourcesToAdd.mSourceMetadata.getBroadcastId()
                                    == broadcastId) {
                                alreadyAdded = true;
                            }
                        }
                        mPendingSourcesToAdd.add(
                                new AddSourceData(sink, sourceMetadata, isGroupOp));
                        if (!alreadyAdded) {
                            addSelectSourceRequest(getCachedBroadcast(broadcastId), true);
                        }
                    }
                } else {
                    log("AddSource: broadcast not cached or invalid, broadcastId: " + broadcastId);
+132 −0
Original line number Diff line number Diff line
@@ -983,6 +983,138 @@ public class BassClientServiceTest {
                        any(), any(), anyInt(), anyInt(), any(), any());
    }

    @Test
    public void testMultipleAddSourceToUnsyncedBroadcaster() {
        mSetFlagsRule.enableFlags(
                Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE);

        prepareConnectedDeviceGroup();
        startSearchingForSources();

        // Scan and sync 1
        onScanResult(mSourceDevice, TEST_BROADCAST_ID);
        InOrder inOrder = inOrder(mMethodProxy);
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
        onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);

        // Stop searching to unsync broadcaster
        mBassClientService.stopSearchingForSources();

        // Sink1 aAdd source to unsynced broadcast, causes synchronization first
        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
        mBassClientService.addSource(mCurrentDevice, meta, false);
        handleHandoverSupport();
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());

        // Sink2 add source to unsynced broadcast
        mBassClientService.addSource(mCurrentDevice1, meta, false);
        handleHandoverSupport();

        // Sync established
        onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
        TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());

        // Both add sources should be called to state machines
        assertThat(mStateMachines.size()).isEqualTo(2);
        for (BassClientStateMachine sm : mStateMachines.values()) {
            ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
            verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());

            Message msg =
                    messageCaptor.getAllValues().stream()
                            .filter(
                                    m ->
                                            (m.what == BassClientStateMachine.ADD_BCAST_SOURCE)
                                                    && (m.obj == meta))
                            .findFirst()
                            .orElse(null);
            assertThat(msg).isNotNull();
        }

        // There should be no second selectSource call
        inOrder.verify(mMethodProxy, never())
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
    }

    @Test
    public void testMultipleAddSourceToUnsyncedInactiveBroadcaster() {
        mSetFlagsRule.enableFlags(
                Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE);

        prepareConnectedDeviceGroup();
        startSearchingForSources();

        // Scan and sync 1
        onScanResult(mSourceDevice, TEST_BROADCAST_ID);
        InOrder inOrder = inOrder(mMethodProxy);
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
        onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);

        // Stop searching to unsync broadcaster
        mBassClientService.stopSearchingForSources();

        // Sink1 aAdd source to unsynced broadcast, causes synchronization first
        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
        mBassClientService.addSource(mCurrentDevice, meta, false);
        handleHandoverSupport();
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());

        // Sink2 add source to unsynced broadcast
        mBassClientService.addSource(mCurrentDevice1, meta, false);
        handleHandoverSupport();

        // Error in syncEstablished causes soureLost, sourceAddFailed notification for both sinks
        BassClientService.PACallback callback = mBassClientService.new PACallback();
        callback.onSyncEstablished(
                TEST_SYNC_HANDLE,
                mSourceDevice,
                TEST_ADVERTISER_SID,
                0,
                200,
                BluetoothGatt.GATT_FAILURE);
        TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
        InOrder inOrderCallback = inOrder(mCallback);
        try {
            inOrderCallback.verify(mCallback).onSourceLost(eq(TEST_BROADCAST_ID));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        try {
            inOrderCallback
                    .verify(mCallback)
                    .onSourceAddFailed(
                            eq(mCurrentDevice),
                            eq(meta),
                            eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        try {
            inOrderCallback
                    .verify(mCallback)
                    .onSourceAddFailed(
                            eq(mCurrentDevice1),
                            eq(meta),
                            eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        // There should be no second selectSource call
        inOrder.verify(mMethodProxy, never())
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
    }

    @Test
    public void testStopSearchingForSources_timeoutForActiveSync() {
        mSetFlagsRule.enableFlags(