Loading android/app/src/com/android/bluetooth/bass_client/BassClientService.java +2 −2 Original line number Diff line number Diff line Loading @@ -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 */ Loading android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java +42 −28 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } } Loading Loading @@ -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(); Loading Loading @@ -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; } Loading android/app/src/com/android/bluetooth/bass_client/BassConstants.java +7 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; } android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java +140 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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( Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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() Loading @@ -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); Loading Loading
android/app/src/com/android/bluetooth/bass_client/BassClientService.java +2 −2 Original line number Diff line number Diff line Loading @@ -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 */ Loading
android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java +42 −28 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } } Loading Loading @@ -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(); Loading Loading @@ -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; } Loading
android/app/src/com/android/bluetooth/bass_client/BassConstants.java +7 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; }
android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java +140 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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( Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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() Loading @@ -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); Loading