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

Commit c3d0fd41 authored by Rongxuan Liu's avatar Rongxuan Liu Committed by Gerrit Code Review
Browse files

Merge "[le audio] Remove external source without local metadata" into main

parents 674fff48 819eb2ab
Loading
Loading
Loading
Loading
+8 −4
Original line number Diff line number Diff line
@@ -3017,14 +3017,18 @@ public class BassClientService extends ProfileService {
                stopBigMonitoring(metaData.getBroadcastId(), true);
            }

            if (metaData != null && stateMachine.isSyncedToTheSource(sourceId)) {
            if (stateMachine.isSyncedToTheSource(sourceId)) {
                sEventLogger.logd(
                        TAG,
                        "Remove Broadcast Source(Force lost PA sync): "
                                + ("device: " + device)
                                + (", sourceId: " + sourceId)
                                + (", broadcastId: " + metaData.getBroadcastId())
                                + (", broadcastName: " + metaData.getBroadcastName()));
                                + (", broadcastId: "
                                        + ((metaData == null)
                                                ? BassConstants.INVALID_BROADCAST_ID
                                                : metaData.getBroadcastId()))
                                + (", broadcastName: "
                                        + ((metaData == null) ? "" : metaData.getBroadcastName())));

                log("Force source to lost PA sync");
                Message message =
@@ -3032,9 +3036,9 @@ public class BassClientService extends ProfileService {
                message.arg1 = sourceId;
                message.arg2 = BassConstants.PA_SYNC_DO_NOT_SYNC;
                /* Pending remove set. Remove source once not synchronized to PA */
                /* MetaData can be null if source is from remote's receive state */
                message.obj = metaData;
                stateMachine.sendMessage(message);

                continue;
            }

+21 −17
Original line number Diff line number Diff line
@@ -1875,7 +1875,7 @@ public class BassClientStateMachine extends StateMachine {
        return res;
    }

    private byte[] convertBroadcastMetadataToUpdateSourceByteArray(
    private byte[] convertToUpdateSourceByteArray(
            int sourceId, BluetoothLeBroadcastMetadata metaData, int paSync) {
        BluetoothLeBroadcastReceiveState existingState =
                getBroadcastReceiveStateForSourceId(sourceId);
@@ -1883,8 +1883,10 @@ public class BassClientStateMachine extends StateMachine {
            log("no existing SI for update source op");
            return null;
        }
        List<BluetoothLeBroadcastSubgroup> subGroups = metaData.getSubgroups();
        byte numSubGroups = (byte) subGroups.size();
        int numSubGroups =
                (metaData != null)
                        ? metaData.getSubgroups().size()
                        : existingState.getNumSubgroups();
        byte[] res = new byte[UPDATE_SOURCE_FIXED_LENGTH + numSubGroups * 5];
        int offset = 0;
        // Opcode
@@ -1904,23 +1906,22 @@ public class BassClientStateMachine extends StateMachine {
        res[offset++] = (byte) 0xFF;
        res[offset++] = (byte) 0xFF;
        // Num_Subgroups
        res[offset++] = numSubGroups;
        res[offset++] = (byte) numSubGroups;

        for (BluetoothLeBroadcastSubgroup subGroup : subGroups) {
            int bisIndexValue;
        for (int i = 0; i < numSubGroups; i++) {
            int bisIndexValue = existingState.getBisSyncState().get(i).intValue();
            if (paSync == BassConstants.PA_SYNC_DO_NOT_SYNC) {
                bisIndexValue = 0;
            } else if (paSync == BassConstants.PA_SYNC_PAST_AVAILABLE
                    || paSync == BassConstants.PA_SYNC_PAST_NOT_AVAILABLE) {
                bisIndexValue = getBisSyncFromChannelPreference(subGroup.getChannels());

            } else if (metaData != null
                    && (paSync == BassConstants.PA_SYNC_PAST_AVAILABLE
                            || paSync == BassConstants.PA_SYNC_PAST_NOT_AVAILABLE)) {
                bisIndexValue =
                        getBisSyncFromChannelPreference(
                                metaData.getSubgroups().get(i).getChannels());
                // Let sink decide to which BIS sync if there is no channel preference
                if (bisIndexValue == 0) {
                    bisIndexValue = 0xFFFFFFFF;
                }
            } else {
                bisIndexValue =
                        existingState.getBisSyncState().get(subGroups.indexOf(subGroup)).intValue();
            }
            log("UPDATE_BCAST_SOURCE: bisIndexValue : " + bisIndexValue);
            // BIS_Sync
@@ -2235,9 +2236,9 @@ public class BassClientStateMachine extends StateMachine {
                    int sourceId = message.arg1;
                    int paSync = message.arg2;
                    log("Updating Broadcast source: " + metaData);
                    // Convert the source from either metadata or remote receive state
                    byte[] updateSourceInfo =
                            convertBroadcastMetadataToUpdateSourceByteArray(
                                    sourceId, metaData, paSync);
                            convertToUpdateSourceByteArray(sourceId, metaData, paSync);
                    if (updateSourceInfo == null) {
                        Log.e(TAG, "update source: source Info is NULL");
                        break;
@@ -2249,7 +2250,9 @@ public class BassClientStateMachine extends StateMachine {
                        if (paSync == BassConstants.PA_SYNC_DO_NOT_SYNC) {
                            setPendingRemove(sourceId, true);
                        }
                        if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) {
                        if (metaData != null
                                && metaData.isEncrypted()
                                && metaData.getBroadcastCode() != null) {
                            mSetBroadcastCodePending = true;
                        }
                        mPendingMetadata = metaData;
@@ -2258,9 +2261,10 @@ public class BassClientStateMachine extends StateMachine {
                                GATT_TXN_TIMEOUT,
                                UPDATE_BCAST_SOURCE,
                                BassConstants.GATT_TXN_TIMEOUT_MS);
                        // convertToUpdateSourceByteArray ensures receive state valid for sourceId
                        sendMessageDelayed(
                                CANCEL_PENDING_SOURCE_OPERATION,
                                metaData.getBroadcastId(),
                                getBroadcastReceiveStateForSourceId(sourceId).getBroadcastId(),
                                BassConstants.SOURCE_OPERATION_TIMEOUT_MS);
                    } else {
                        Log.e(TAG, "UPDATE_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal");
+51 −0
Original line number Diff line number Diff line
@@ -1868,6 +1868,57 @@ public class BassClientServiceTest {
        }
    }

    /**
     * Test whether service.removeSource() does send modify source if source is from remote receive
     * state. In this case, assistant should be able to remove source which was not managed by BASS
     * service (external manager/no source metadata)
     */
    @Test
    public void testRemoveSourceForGroupAndTriggerModifySourceWithoutMetadata() {
        prepareConnectedDeviceGroup();
        startSearchingForSources();
        onScanResult(mSourceDevice, TEST_BROADCAST_ID);
        onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);

        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);
            // no current broadcast metadata for external broadcast source
            doReturn(null).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
            doReturn(true).when(sm).isSyncedToTheSource(eq(TEST_SOURCE_ID));
        }

        for (BassClientStateMachine sm : mStateMachines.values()) {
            ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
            mBassClientService.removeSource(sm.getDevice(), TEST_SOURCE_ID);
            // Verify device get update source
            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);

            assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID);
            assertThat(msg.get().arg2).isEqualTo(BassConstants.PA_SYNC_DO_NOT_SYNC);
            // Verify metadata is null
            assertThat(msg.get().obj).isEqualTo(null);
        }

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

    /** Test whether the group operation flag is set on addSource() and removed on removeSource */
    @Test
    public void testGroupStickyFlagSetUnset() {
+34 −18
Original line number Diff line number Diff line
@@ -1583,23 +1583,6 @@ public class BassClientStateMachineTest {
                    0x00, // broadcastIdBytes
                    (byte) BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
                    (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED,
                    // 16 bytes badBroadcastCode
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x00,
                    0x01, // numSubGroups
                    // SubGroup #1
                    0x00,
@@ -2405,7 +2388,7 @@ public class BassClientStateMachineTest {
                    Utils.getByteAddress(mSourceTestDevice)[1],
                    Utils.getByteAddress(mSourceTestDevice)[0], // sourceAddress
                    0x00, // sourceAdvSid
                    0x00,
                    (byte) (TEST_BROADCAST_ID & 0xFF),
                    0x00,
                    0x00, // broadcastIdBytes
                    (byte) paSync,
@@ -2648,6 +2631,39 @@ public class BassClientStateMachineTest {
        verify(callbacks).notifyBassStateReady(eq(mTestDevice));
    }

    @Test
    public void updateBroadcastSource_withoutMetadata() {
        int sourceId = 1;
        int paSync = BassConstants.PA_SYNC_DO_NOT_SYNC;

        prepareInitialReceiveStateForGatt();

        generateBroadcastReceiveStatesAndVerify(
                mSourceTestDevice,
                TEST_SOURCE_ID,
                BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
                BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING,
                0x1L);

        BassClientStateMachine.BluetoothGattTestableWrapper btGatt =
                Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class);
        mBassClientStateMachine.mBluetoothGatt = btGatt;
        BluetoothGattCharacteristic scanControlPoint =
                Mockito.mock(BluetoothGattCharacteristic.class);
        mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint;

        mBassClientStateMachine.mPendingOperation = 0;
        mBassClientStateMachine.mPendingSourceId = 0;
        mBassClientStateMachine.mPendingMetadata = null;

        // update source without metadata
        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(UPDATE_BCAST_SOURCE, sourceId, paSync, null),
                BassClientStateMachine.ConnectedProcessing.class);
        assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(UPDATE_BCAST_SOURCE);
        assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(sourceId);
    }

    private void initToConnectingState() {
        allowConnection(true);
        allowConnectGatt(true);