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

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

Merge changes I99983883,Iaee9682c into main

* changes:
  Bass: Refactor bis sync operation usage
  [le audio] Fix modifying source for BIS sync control
parents 6ba5ce23 a6001bc3
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -4191,8 +4191,8 @@ public class BassClientService extends ProfileService {
    }

    private boolean isSyncedToBroadcastStream(Long syncState) {
        return syncState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS
                && syncState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG;
        return syncState != BassConstants.BCAST_RCVR_STATE_BIS_SYNC_NOT_SYNC_TO_BIS
                && syncState != BassConstants.BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG;
    }

    /** Handle broadcast state changed */
+42 −28
Original line number Diff line number Diff line
@@ -939,15 +939,15 @@ class BassClientStateMachine extends StateMachine {
        // Check Bis state
        for (int i = 0; i < recvState.getNumSubgroups(); i++) {
            Long bisState = recvState.getBisSyncState().get(i);
            if (bisState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG
                    && bisState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS) {
            if (bisState != BassConstants.BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG
                    && bisState != BassConstants.BCAST_RCVR_STATE_BIS_SYNC_NOT_SYNC_TO_BIS) {
                // Any bis synced, update status and break
                syncStats.updateBisSyncedTime(SystemClock.elapsedRealtime());
                syncStats.updateSyncStatus(
                        BluetoothStatsLog
                                .BROADCAST_AUDIO_SYNC_REPORTED__SYNC_STATUS__SYNC_STATUS_AUDIO_SYNC_SUCCESS);
                break;
            } else if (bisState == BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG) {
            } else if (bisState == BassConstants.BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG) {
                logBroadcastSyncStatsWithStatus(
                        broadcastId,
                        BluetoothStatsLog
@@ -2049,15 +2049,16 @@ class BassClientStateMachine extends StateMachine {
        }
    }

    private static int getBisSyncFromChannelPreference(List<BluetoothLeBroadcastChannel> channels) {
        int bisSync = 0;
    private static long getBisSyncFromChannelPreference(
            List<BluetoothLeBroadcastChannel> channels) {
        long bisSync = 0L;
        for (BluetoothLeBroadcastChannel channel : channels) {
            if (channel.isSelected()) {
                if (channel.getChannelIndex() == 0) {
                    Log.e(TAG, "getBisSyncFromChannelPreference: invalid channel index=0");
                    continue;
                }
                bisSync |= 1 << (channel.getChannelIndex() - 1);
                bisSync |= 1L << (channel.getChannelIndex() - 1);
            }
        }

@@ -2106,14 +2107,14 @@ class BassClientStateMachine extends StateMachine {

        for (BluetoothLeBroadcastSubgroup subGroup : subGroups) {
            // BIS_Sync
            int bisSync = getBisSyncFromChannelPreference(subGroup.getChannels());
            if (bisSync == 0) {
                bisSync = 0xFFFFFFFF;
            long bisSync = getBisSyncFromChannelPreference(subGroup.getChannels());
            if (bisSync == BassConstants.BIS_SYNC_DO_NOT_SYNC_TO_BIS) {
                bisSync = BassConstants.BIS_SYNC_NO_PREFERENCE;
            }
            stream.write(bisSync & 0x00000000000000FF);
            stream.write((bisSync & 0x000000000000FF00) >>> 8);
            stream.write((bisSync & 0x0000000000FF0000) >>> 16);
            stream.write((bisSync & 0x00000000FF000000) >>> 24);
            stream.write((byte) (bisSync & 0x00000000000000FFL));
            stream.write((byte) ((bisSync & 0x000000000000FF00L) >>> 8));
            stream.write((byte) ((bisSync & 0x0000000000FF0000L) >>> 16));
            stream.write((byte) ((bisSync & 0x00000000FF000000L) >>> 24));

            // Metadata_Length
            BluetoothLeAudioContentMetadata metadata = subGroup.getContentMetadata();
@@ -2162,28 +2163,41 @@ class BassClientStateMachine extends StateMachine {
        res[offset++] = (byte) numSubGroups;

        for (int i = 0; i < numSubGroups; i++) {
            int bisIndexValue = 0xFFFFFFFF;
            long bisIndexValue = BassConstants.BIS_SYNC_NO_PREFERENCE;
            long currentBisIndexValue = BassConstants.BIS_SYNC_NO_PREFERENCE;
            if (i < existingState.getBisSyncState().size()) {
                currentBisIndexValue = existingState.getBisSyncState().get(i);
            }

            if (paSync == BassConstants.PA_SYNC_DO_NOT_SYNC) {
                bisIndexValue = 0;
            } else if (metaData != null
                    && (paSync == BassConstants.PA_SYNC_PAST_AVAILABLE
                            || paSync == BassConstants.PA_SYNC_PAST_NOT_AVAILABLE)) {
                bisIndexValue = BassConstants.BIS_SYNC_DO_NOT_SYNC_TO_BIS;
            } else if (metaData != null) {
                bisIndexValue =
                        getBisSyncFromChannelPreference(
                                metaData.getSubgroups().get(i).getChannels());
                // If updating metadata with paSync INVALID_PA_SYNC_VALUE
                // Use bisIndexValue parsed from metadata channels
                if (paSync == BassConstants.PA_SYNC_PAST_AVAILABLE
                        || paSync == BassConstants.PA_SYNC_PAST_NOT_AVAILABLE) {
                    // Let sink decide to which BIS sync if there is no channel preference
                if (bisIndexValue == 0) {
                    bisIndexValue = 0xFFFFFFFF;
                    if (bisIndexValue == BassConstants.BIS_SYNC_DO_NOT_SYNC_TO_BIS) {
                        bisIndexValue = BassConstants.BIS_SYNC_NO_PREFERENCE;
                    }
            } else if (i < existingState.getBisSyncState().size()) {
                bisIndexValue = existingState.getBisSyncState().get(i).intValue();
                }
            log("UPDATE_BCAST_SOURCE: bisIndexValue : " + bisIndexValue);
            } else {
                // Keep using BIS index from remote receive state
                bisIndexValue = currentBisIndexValue;
            }
            log(
                    "UPDATE_BCAST_SOURCE: bisIndexValue from: "
                            + currentBisIndexValue
                            + " to: "
                            + bisIndexValue);
            // BIS_Sync
            res[offset++] = (byte) (bisIndexValue & 0x00000000000000FF);
            res[offset++] = (byte) ((bisIndexValue & 0x000000000000FF00) >>> 8);
            res[offset++] = (byte) ((bisIndexValue & 0x0000000000FF0000) >>> 16);
            res[offset++] = (byte) ((bisIndexValue & 0x00000000FF000000) >>> 24);
            res[offset++] = (byte) (bisIndexValue & 0x00000000000000FFL);
            res[offset++] = (byte) ((bisIndexValue & 0x000000000000FF00L) >>> 8);
            res[offset++] = (byte) ((bisIndexValue & 0x0000000000FF0000L) >>> 16);
            res[offset++] = (byte) ((bisIndexValue & 0x00000000FF000000L) >>> 24);
            // Metadata_Length; On Modify source, don't update any Metadata
            res[offset++] = 0;
        }
+7 −4
Original line number Diff line number Diff line
@@ -62,6 +62,9 @@ public class BassConstants {
    public static final int BCAST_RCVR_STATE_BADCODE_START_IDX = 14;
    public static final int BCAST_RCVR_STATE_BADCODE_SIZE = 16;
    public static final int BCAST_RCVR_STATE_BIS_SYNC_SIZE = 4;
    // BIS_Sync State value
    public static final long BCAST_RCVR_STATE_BIS_SYNC_NOT_SYNC_TO_BIS = 0x00000000L;
    public static final long BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG = 0xFFFFFFFFL;
    // 30 secs time out for all gatt writes
    public static final int GATT_TXN_TIMEOUT_MS = 30000;
    public static final int SOURCE_OPERATION_TIMEOUT_MS = 3000;
@@ -75,11 +78,11 @@ public class BassConstants {
    public static final int BCAST_NAME_AD_TYPE = 0x30;
    public static final int BCAST_NAME_LEN_MIN = 4;
    public static final int BCAST_NAME_LEN_MAX = 32;
    // PA_Sync parameter value
    // PA_Sync parameter value in BASS operations
    public static final int PA_SYNC_DO_NOT_SYNC = 0x00;
    public static final int PA_SYNC_PAST_AVAILABLE = 0x01;
    public static final int PA_SYNC_PAST_NOT_AVAILABLE = 0x02;
    // BIS_Sync parameter value
    public static final long BIS_SYNC_NOT_SYNC_TO_BIS = 0x00000000L;
    public static final long BIS_SYNC_FAILED_SYNC_TO_BIG = 0xFFFFFFFFL;
    // BIS_Sync parameter value in BASS operations
    public static final long BIS_SYNC_DO_NOT_SYNC_TO_BIS = 0x00000000L;
    public static final long BIS_SYNC_NO_PREFERENCE = 0xFFFFFFFFL;
}
+140 −3
Original line number Diff line number Diff line
@@ -145,8 +145,11 @@ public class BassClientStateMachineTest {
    private static final int WAIT_MS = 1_200;
    private static final int TEST_BROADCAST_ID = 42;
    private static final int TEST_SOURCE_ID = 1;
    private static final int TEST_CHANNEL_INDEX = 1;
    private static final String TEST_BROADCAST_NAME = "Test";
    private static final String EMPTY_BLUETOOTH_DEVICE_ADDRESS = "00:00:00:00:00:00";
    private static final byte OPCODE_UPDATE_SOURCE = 0x03;
    private static final int UPDATE_SOURCE_FIXED_LENGTH = 6;
    private Context mTargetContext;
    private BluetoothAdapter mAdapter;
    private HandlerThread mHandlerThread;
@@ -2978,7 +2981,7 @@ public class BassClientStateMachineTest {
                TEST_SOURCE_ID,
                BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
                BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING,
                BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG);
                BassConstants.BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG);
        // Verify broadcast audio session is logged when bis sync failed
        verify(mMetricsLogger)
                .logLeAudioBroadcastAudioSync(
@@ -3179,6 +3182,69 @@ public class BassClientStateMachineTest {
        assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(TEST_SOURCE_ID);
    }

    @Test
    public void updateBroadcastSource_withMetadataChanged() {
        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;

        BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata();
        mBassClientStateMachine.mPendingMetadata = metadata;

        // Verify pausing broadcast stream with updated metadata
        BluetoothLeBroadcastMetadata updatedMetadataPaused = getMetadataToPauseStream(metadata);
        byte[] valueBisPaused = convertMetadataToUpdateSourceByteArray(updatedMetadataPaused);

        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(
                        UPDATE_BCAST_SOURCE,
                        TEST_SOURCE_ID,
                        BassConstants.INVALID_PA_SYNC_VALUE,
                        updatedMetadataPaused),
                BassClientStateMachine.ConnectedProcessing.class);
        assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(UPDATE_BCAST_SOURCE);
        assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(TEST_SOURCE_ID);
        verify(scanControlPoint).setValue(eq(valueBisPaused));

        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED),
                BassClientStateMachine.Connected.class);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        Mockito.clearInvocations(scanControlPoint);

        // Verify resuming broadcast stream with the original metadata
        byte[] valueBisResumed = convertMetadataToUpdateSourceByteArray(metadata);
        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(
                        UPDATE_BCAST_SOURCE,
                        TEST_SOURCE_ID,
                        BassConstants.INVALID_PA_SYNC_VALUE,
                        metadata),
                BassClientStateMachine.ConnectedProcessing.class);
        assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(UPDATE_BCAST_SOURCE);
        assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(TEST_SOURCE_ID);
        verify(scanControlPoint).setValue(eq(valueBisResumed));

        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED),
                BassClientStateMachine.Connected.class);

        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        Mockito.clearInvocations(scanControlPoint);
    }

    private void initToConnectingState() {
        allowConnection(true);
        allowConnectGatt(true);
@@ -3284,6 +3350,78 @@ public class BassClientStateMachineTest {
        return builder.build();
    }

    private byte[] convertMetadataToUpdateSourceByteArray(BluetoothLeBroadcastMetadata metaData) {
        int numSubGroups = metaData.getSubgroups().size();

        byte[] res = new byte[UPDATE_SOURCE_FIXED_LENGTH + numSubGroups * 5];
        int offset = 0;
        // Opcode
        res[offset++] = OPCODE_UPDATE_SOURCE;
        // Source_ID
        res[offset++] = (byte) TEST_SOURCE_ID;
        // PA_Sync
        res[offset++] = (byte) (0x01);
        // PA_Interval
        res[offset++] = (byte) 0xFF;
        res[offset++] = (byte) 0xFF;
        // Num_Subgroups
        res[offset++] = (byte) numSubGroups;

        for (int i = 0; i < numSubGroups; i++) {
            int bisIndexValue = 0;
            for (BluetoothLeBroadcastChannel channel :
                    metaData.getSubgroups().get(i).getChannels()) {
                if (channel.isSelected()) {
                    if (channel.getChannelIndex() == 0) {
                        continue;
                    }
                    bisIndexValue |= 1 << (channel.getChannelIndex() - 1);
                }
            }
            // BIS_Sync
            res[offset++] = (byte) (bisIndexValue & 0x00000000000000FF);
            res[offset++] = (byte) ((bisIndexValue & 0x000000000000FF00) >>> 8);
            res[offset++] = (byte) ((bisIndexValue & 0x0000000000FF0000) >>> 16);
            res[offset++] = (byte) ((bisIndexValue & 0x00000000FF000000) >>> 24);
            // Metadata_Length; On Modify source, don't update any Metadata
            res[offset++] = 0;
        }
        return res;
    }

    private BluetoothLeBroadcastMetadata getMetadataToPauseStream(
            BluetoothLeBroadcastMetadata metadata) {
        BluetoothLeBroadcastMetadata.Builder metadataToUpdateBuilder =
                new BluetoothLeBroadcastMetadata.Builder(metadata);

        List<BluetoothLeBroadcastSubgroup> updatedSubgroups = new ArrayList<>();
        for (BluetoothLeBroadcastSubgroup subgroup : metadata.getSubgroups()) {
            BluetoothLeBroadcastSubgroup.Builder subgroupBuilder =
                    new BluetoothLeBroadcastSubgroup.Builder(subgroup);

            List<BluetoothLeBroadcastChannel> updatedChannels = new ArrayList<>();
            for (BluetoothLeBroadcastChannel channel : subgroup.getChannels()) {
                BluetoothLeBroadcastChannel updatedChannel =
                        new BluetoothLeBroadcastChannel.Builder(channel).setSelected(false).build();
                updatedChannels.add(updatedChannel);
            }

            subgroupBuilder.clearChannel();
            for (BluetoothLeBroadcastChannel channel : updatedChannels) {
                subgroupBuilder.addChannel(channel);
            }

            updatedSubgroups.add(subgroupBuilder.build());
        }

        metadataToUpdateBuilder.clearSubgroup();
        for (BluetoothLeBroadcastSubgroup subgroup : updatedSubgroups) {
            metadataToUpdateBuilder.addSubgroup(subgroup);
        }

        return metadataToUpdateBuilder.build();
    }

    private void prepareInitialReceiveStateForGatt() {
        initToConnectedState();
        mBassClientStateMachine.connectGatt(true);
@@ -3416,7 +3554,6 @@ public class BassClientStateMachineTest {
        // German language code in ISO 639-3
        final String testLanguage = "deu";
        final int testCodecId = 42;
        final int testChannelIndex = 56;

        BluetoothLeAudioCodecConfigMetadata codecMetadata =
                new BluetoothLeAudioCodecConfigMetadata.Builder()
@@ -3442,7 +3579,7 @@ public class BassClientStateMachineTest {
        BluetoothLeBroadcastChannel channel =
                new BluetoothLeBroadcastChannel.Builder()
                        .setSelected(true)
                        .setChannelIndex(testChannelIndex)
                        .setChannelIndex(TEST_CHANNEL_INDEX)
                        .setCodecMetadata(channelCodecMetadata)
                        .build();
        builder.addChannel(channel);