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

Commit 4315ba6c authored by Rongxuan Liu's avatar Rongxuan Liu
Browse files

[le audio] Remove source should trigger update first if BIS is synced

If the sinks are actively synced to source, the remove source request will trigger modify source operation if PA synced.

We should also do the same if PA is un-synced but BIS is synced since some buds might choose to unsync PA and only sync to BIS to save power.

Bug: 321292454
Bug: 316005152
Test: atest BassClientServiceTest BassClientStateMachineTest
Change-Id: I357de25a8a12cc6489d7475c3b51fbaa3f9ead57
parent 0d9d5356
Loading
Loading
Loading
Loading
+2 −5
Original line number Diff line number Diff line
@@ -59,8 +59,8 @@ import com.android.bluetooth.flags.FeatureFlagsImpl;
import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
@@ -1583,12 +1583,9 @@ public class BassClientService extends ProfileService {
                continue;
            }

            BluetoothLeBroadcastReceiveState recvState =
                    stateMachine.getBroadcastReceiveStateForSourceId(sourceId);
            BluetoothLeBroadcastMetadata metaData =
                    stateMachine.getCurrentBroadcastMetadata(sourceId);
            if (metaData != null && recvState != null && recvState.getPaSyncState()
                    == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED) {
            if (metaData != null && stateMachine.isSyncedToTheSource(sourceId)) {
                sEventLogger.logd(
                        DBG,
                        TAG,
+14 −7
Original line number Diff line number Diff line
@@ -325,6 +325,19 @@ public class BassClientStateMachine extends StateMachine {
        return null;
    }

    boolean isSyncedToTheSource(int sourceId) {
        BluetoothLeBroadcastReceiveState recvState = getBroadcastReceiveStateForSourceId(sourceId);

        return recvState != null
                && (recvState.getPaSyncState()
                                == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED
                        || recvState.getBisSyncState().stream()
                                .anyMatch(
                                        bitmap -> {
                                            return bitmap != 0;
                                        }));
    }

    void parseBaseData(BluetoothDevice device, int syncHandle, byte[] serviceData) {
        log("parseBaseData" + Arrays.toString(serviceData));
        BaseData base = BaseData.parseBaseData(serviceData);
@@ -1759,15 +1772,9 @@ public class BassClientStateMachine extends StateMachine {
                    // Save pending source to be added once existing source got removed
                    mPendingSourceToSwitch = metaData;
                    // Remove the source first
                    BluetoothLeBroadcastReceiveState recvStateToUpdate =
                            getBroadcastReceiveStateForSourceId(sourceIdToRemove);
                    BluetoothLeBroadcastMetadata metaDataToUpdate =
                            getCurrentBroadcastMetadata(sourceIdToRemove);
                    if (metaDataToUpdate != null
                            && recvStateToUpdate != null
                            && recvStateToUpdate.getPaSyncState()
                                    == BluetoothLeBroadcastReceiveState
                                            .PA_SYNC_STATE_SYNCHRONIZED) {
                    if (metaDataToUpdate != null && isSyncedToTheSource(sourceIdToRemove)) {
                        log("SWITCH_BCAST_SOURCE force source to lost PA sync");
                        Message msg = obtainMessage(UPDATE_BCAST_SOURCE);
                        msg.arg1 = sourceIdToRemove;
+83 −0
Original line number Diff line number Diff line
@@ -751,6 +751,89 @@ public class BassClientServiceTest {
        }
    }

    /**
     * Test whether service.removeSource() does send modify source to all the state machines if
     * either PA or BIS is synced
     */
    @Test
    public void testRemoveSourceForGroupAndTriggerModifySource() {
        prepareConnectedDeviceGroup();
        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
        verifyAddSourceForGroup(meta);
        for (BassClientStateMachine sm : mStateMachines.values()) {
            injectRemoteSourceStateSourceAdded(
                    sm,
                    meta,
                    TEST_SOURCE_ID,
                    BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
                    meta.isEncrypted()
                            ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
                            : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
                    null);
            doReturn(meta).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
            doReturn(true).when(sm).isSyncedToTheSource(eq(TEST_SOURCE_ID));
        }

        // Remove broadcast source
        mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);

        // Verify all group members getting UPDATE_BCAST_SOURCE message
        // because PA state is synced
        assertThat(mStateMachines.size()).isEqualTo(2);
        for (BassClientStateMachine sm : mStateMachines.values()) {
            ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
            verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());

            Optional<Message> msg =
                    messageCaptor.getAllValues().stream()
                            .filter(m -> m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE)
                            .findFirst();
            assertThat(msg.isPresent()).isEqualTo(true);

            // Verify using the right sourceId on each device
            assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID);
        }

        for (BassClientStateMachine sm : mStateMachines.values()) {
            // Update receiver state
            injectRemoteSourceStateChanged(
                    sm,
                    meta,
                    TEST_SOURCE_ID,
                    BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
                    meta.isEncrypted()
                            ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
                            : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
                    null,
                    (long) 0x00000001);
            verify(mLeAudioService).activeBroadcastAssistantNotification(eq(true));
        }

        // Remove broadcast source
        mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);

        // Verify all group members getting UPDATE_BCAST_SOURCE message if
        // bis sync state is non-zero and pa sync state is not synced
        assertThat(mStateMachines.size()).isEqualTo(2);
        for (BassClientStateMachine sm : mStateMachines.values()) {
            ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
            verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());

            Optional<Message> msg =
                    messageCaptor.getAllValues().stream()
                            .filter(m -> m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE)
                            .findFirst();
            assertThat(msg.isPresent()).isEqualTo(true);

            // Verify using the right sourceId on each device
            assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID);
        }

        for (BassClientStateMachine sm : mStateMachines.values()) {
            injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID);
        }
    }

    /**
     * Test whether the group operation flag is set on addSource() and removed on removeSource
     */