Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +16 −35 Original line number Diff line number Diff line Loading @@ -2607,6 +2607,11 @@ public class LeAudioService extends ProfileService { void transitionFromBroadcastToUnicast() { if (mUnicastGroupIdDeactivatedForBroadcastTransition == LE_AUDIO_GROUP_ID_INVALID) { Log.d(TAG, "No deactivated group due for broadcast transmission"); // Notify audio manager if (mBroadcastDescriptors.values().stream() .noneMatch(d -> d.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING))) { updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); } return; } Loading Loading @@ -3031,22 +3036,7 @@ public class LeAudioService extends ProfileService { notifyPlaybackStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); /* Restore the Unicast stream from before the Broadcast was started. */ if (mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID) { transitionFromBroadcastToUnicast(); } else { // Notify audio manager if (mBroadcastDescriptors.values().stream() .noneMatch( d -> d.mState.equals( LeAudioStackEvent .BROADCAST_STATE_STREAMING))) { updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); } } destroyBroadcast(broadcastId); break; case LeAudioStackEvent.BROADCAST_STATE_CONFIGURING: Loading @@ -3068,21 +3058,7 @@ public class LeAudioService extends ProfileService { bassClientService.suspendReceiversSourceSynchronization(broadcastId); } /* Restore the Unicast stream from before the Broadcast was started. */ if (mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID) { transitionFromBroadcastToUnicast(); } else { // Notify audio manager if (mBroadcastDescriptors.values().stream() .noneMatch( d -> d.mState.equals( LeAudioStackEvent .BROADCAST_STATE_STREAMING))) { updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); } } break; case LeAudioStackEvent.BROADCAST_STATE_STOPPING: Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " stopping."); Loading Loading @@ -4382,6 +4358,10 @@ public class LeAudioService extends ProfileService { return; } if (Flags.leaudioBroadcastDestroyAfterTimeout()) { transitionFromBroadcastToUnicast(); destroyBroadcast(mBroadcastId); } else { if (mActiveBroadcastAudioDevice != null) { updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); } Loading @@ -4389,6 +4369,7 @@ public class LeAudioService extends ProfileService { notifyBroadcastStartFailed(BluetoothStatusCodes.ERROR_TIMEOUT); } } } /** * Binder object: must be a static class or memory leak may occur Loading android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java +56 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ import java.util.concurrent.TimeoutException; @RunWith(AndroidJUnit4.class) public class LeAudioBroadcastServiceTest { private static final int TIMEOUT_MS = 1000; private static final int CREATE_BROADCAST_TIMEOUT_MS = 6000; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); Loading Loading @@ -430,6 +431,61 @@ public class LeAudioBroadcastServiceTest { Assert.assertTrue(mOnBroadcastStartFailedCalled); } @Test public void testCreateBroadcastTimeout() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_DESTROY_AFTER_TIMEOUT); int broadcastId = 243; byte[] code = {0x00, 0x01, 0x00, 0x02}; mService.mBroadcastCallbacks.register(mCallbacks); BluetoothLeAudioContentMetadata.Builder meta_builder = new BluetoothLeAudioContentMetadata.Builder(); meta_builder.setLanguage("deu"); meta_builder.setProgramInfo("Public broadcast info"); BluetoothLeAudioContentMetadata meta = meta_builder.build(); BluetoothLeBroadcastSettings settings = buildBroadcastSettingsFromMetadata(meta, code, 1); mService.createBroadcast(settings); // Test data with only one subgroup int[] expectedQualityArray = {settings.getSubgroupSettings().get(0).getPreferredQuality()}; byte[][] expectedDataArray = { settings.getSubgroupSettings().get(0).getContentMetadata().getRawMetadata() }; verify(mLeAudioBroadcasterNativeInterface, times(1)) .createBroadcast( eq(true), eq(TEST_BROADCAST_NAME), eq(code), eq(settings.getPublicBroadcastMetadata().getRawMetadata()), eq(expectedQualityArray), eq(expectedDataArray)); // Check if broadcast is started automatically when created LeAudioStackEvent create_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED); create_event.valueInt1 = broadcastId; create_event.valueBool1 = true; mService.messageFromNative(create_event); // Verify if broadcast is auto-started on start verify(mLeAudioBroadcasterNativeInterface, times(1)).startBroadcast(eq(broadcastId)); Assert.assertTrue(mOnBroadcastStartedCalled); // Notify initial paused state LeAudioStackEvent state_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE); state_event.valueInt1 = broadcastId; state_event.valueInt2 = LeAudioStackEvent.BROADCAST_STATE_PAUSED; mService.messageFromNative(state_event); // Check if broadcast is destroyed after timeout verify(mLeAudioBroadcasterNativeInterface, timeout(CREATE_BROADCAST_TIMEOUT_MS).times(1)) .destroyBroadcast(eq(broadcastId)); } @Test public void testCreateBroadcast_updateQualityToStandard() { byte[] code = {0x00, 0x01, 0x00, 0x02}; Loading flags/leaudio.aconfig +11 −1 Original line number Diff line number Diff line Loading @@ -289,3 +289,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "leaudio_broadcast_destroy_after_timeout" namespace: "bluetooth" description: "Destroy broadcast if it fails to start in time" bug: "332500141" metadata { purpose: PURPOSE_BUGFIX } } Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +16 −35 Original line number Diff line number Diff line Loading @@ -2607,6 +2607,11 @@ public class LeAudioService extends ProfileService { void transitionFromBroadcastToUnicast() { if (mUnicastGroupIdDeactivatedForBroadcastTransition == LE_AUDIO_GROUP_ID_INVALID) { Log.d(TAG, "No deactivated group due for broadcast transmission"); // Notify audio manager if (mBroadcastDescriptors.values().stream() .noneMatch(d -> d.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING))) { updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); } return; } Loading Loading @@ -3031,22 +3036,7 @@ public class LeAudioService extends ProfileService { notifyPlaybackStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); /* Restore the Unicast stream from before the Broadcast was started. */ if (mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID) { transitionFromBroadcastToUnicast(); } else { // Notify audio manager if (mBroadcastDescriptors.values().stream() .noneMatch( d -> d.mState.equals( LeAudioStackEvent .BROADCAST_STATE_STREAMING))) { updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); } } destroyBroadcast(broadcastId); break; case LeAudioStackEvent.BROADCAST_STATE_CONFIGURING: Loading @@ -3068,21 +3058,7 @@ public class LeAudioService extends ProfileService { bassClientService.suspendReceiversSourceSynchronization(broadcastId); } /* Restore the Unicast stream from before the Broadcast was started. */ if (mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID) { transitionFromBroadcastToUnicast(); } else { // Notify audio manager if (mBroadcastDescriptors.values().stream() .noneMatch( d -> d.mState.equals( LeAudioStackEvent .BROADCAST_STATE_STREAMING))) { updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); } } break; case LeAudioStackEvent.BROADCAST_STATE_STOPPING: Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " stopping."); Loading Loading @@ -4382,6 +4358,10 @@ public class LeAudioService extends ProfileService { return; } if (Flags.leaudioBroadcastDestroyAfterTimeout()) { transitionFromBroadcastToUnicast(); destroyBroadcast(mBroadcastId); } else { if (mActiveBroadcastAudioDevice != null) { updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); } Loading @@ -4389,6 +4369,7 @@ public class LeAudioService extends ProfileService { notifyBroadcastStartFailed(BluetoothStatusCodes.ERROR_TIMEOUT); } } } /** * Binder object: must be a static class or memory leak may occur Loading
android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java +56 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ import java.util.concurrent.TimeoutException; @RunWith(AndroidJUnit4.class) public class LeAudioBroadcastServiceTest { private static final int TIMEOUT_MS = 1000; private static final int CREATE_BROADCAST_TIMEOUT_MS = 6000; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); Loading Loading @@ -430,6 +431,61 @@ public class LeAudioBroadcastServiceTest { Assert.assertTrue(mOnBroadcastStartFailedCalled); } @Test public void testCreateBroadcastTimeout() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_DESTROY_AFTER_TIMEOUT); int broadcastId = 243; byte[] code = {0x00, 0x01, 0x00, 0x02}; mService.mBroadcastCallbacks.register(mCallbacks); BluetoothLeAudioContentMetadata.Builder meta_builder = new BluetoothLeAudioContentMetadata.Builder(); meta_builder.setLanguage("deu"); meta_builder.setProgramInfo("Public broadcast info"); BluetoothLeAudioContentMetadata meta = meta_builder.build(); BluetoothLeBroadcastSettings settings = buildBroadcastSettingsFromMetadata(meta, code, 1); mService.createBroadcast(settings); // Test data with only one subgroup int[] expectedQualityArray = {settings.getSubgroupSettings().get(0).getPreferredQuality()}; byte[][] expectedDataArray = { settings.getSubgroupSettings().get(0).getContentMetadata().getRawMetadata() }; verify(mLeAudioBroadcasterNativeInterface, times(1)) .createBroadcast( eq(true), eq(TEST_BROADCAST_NAME), eq(code), eq(settings.getPublicBroadcastMetadata().getRawMetadata()), eq(expectedQualityArray), eq(expectedDataArray)); // Check if broadcast is started automatically when created LeAudioStackEvent create_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED); create_event.valueInt1 = broadcastId; create_event.valueBool1 = true; mService.messageFromNative(create_event); // Verify if broadcast is auto-started on start verify(mLeAudioBroadcasterNativeInterface, times(1)).startBroadcast(eq(broadcastId)); Assert.assertTrue(mOnBroadcastStartedCalled); // Notify initial paused state LeAudioStackEvent state_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE); state_event.valueInt1 = broadcastId; state_event.valueInt2 = LeAudioStackEvent.BROADCAST_STATE_PAUSED; mService.messageFromNative(state_event); // Check if broadcast is destroyed after timeout verify(mLeAudioBroadcasterNativeInterface, timeout(CREATE_BROADCAST_TIMEOUT_MS).times(1)) .destroyBroadcast(eq(broadcastId)); } @Test public void testCreateBroadcast_updateQualityToStandard() { byte[] code = {0x00, 0x01, 0x00, 0x02}; Loading
flags/leaudio.aconfig +11 −1 Original line number Diff line number Diff line Loading @@ -289,3 +289,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "leaudio_broadcast_destroy_after_timeout" namespace: "bluetooth" description: "Destroy broadcast if it fails to start in time" bug: "332500141" metadata { purpose: PURPOSE_BUGFIX } }