Loading android/app/src/com/android/bluetooth/bass_client/BassClientService.java +45 −17 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask; import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState; import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment; import static com.android.bluetooth.flags.Flags.leaudioBroadcastAudioHandoverPolicies; import static com.android.bluetooth.flags.Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine; Loading Loading @@ -2424,6 +2425,20 @@ public class BassClientService extends ProfileService { if (leaudioBroadcastAssistantPeripheralEntrustment()) { if (isLocalBroadcast(sourceMetadata)) { LeAudioService leAudioService = mServiceFactory.getLeAudioService(); if (leaudioBigDependsOnAudioState()) { if (leAudioService == null || !(leAudioService.isPaused(sourceMetadata.getBroadcastId()) || leAudioService.isPlaying(sourceMetadata.getBroadcastId()))) { Log.w(TAG, "addSource: Local source can't be add"); mCallbacks.notifySourceAddFailed( sink, sourceMetadata, BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES); return; } } else { if (leAudioService == null || !leAudioService.isPlaying(sourceMetadata.getBroadcastId())) { Log.w(TAG, "addSource: Local source can't be add"); Loading @@ -2436,6 +2451,7 @@ public class BassClientService extends ProfileService { return; } } } } else { if (!isAllowedToAddSource()) { Log.d(TAG, "Add source to pending list"); Loading Loading @@ -2963,17 +2979,29 @@ public class BassClientService extends ProfileService { return; } for (Map.Entry<Integer, HashSet<BluetoothDevice>> entry : mLocalBroadcastReceivers.entrySet()) { Iterator<Map.Entry<Integer, HashSet<BluetoothDevice>>> iterator = mLocalBroadcastReceivers.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Integer, HashSet<BluetoothDevice>> entry = iterator.next(); Integer broadcastId = entry.getKey(); HashSet<BluetoothDevice> devices = entry.getValue(); if (leaudioBigDependsOnAudioState()) { /* If somehow there is a non configured/playing broadcast, let's remove it */ if (!(leAudioService.isPaused(broadcastId) || leAudioService.isPlaying(broadcastId))) { Log.w(TAG, "Non playing broadcast remove from receivers list"); iterator.remove(); continue; } } else { /* If somehow there is a non playing broadcast, let's remove it */ if (!leAudioService.isPlaying(broadcastId)) { Log.w(TAG, "Non playing broadcast remove from receivers list"); mLocalBroadcastReceivers.remove(broadcastId); iterator.remove(); continue; } } if (isIntentional) { /* Check if disconnecting device participated in this broadcast reception */ Loading @@ -2998,7 +3026,7 @@ public class BassClientService extends ProfileService { + "(broadcast ID: " + broadcastId + ") receivers - stopping broadcast"); mLocalBroadcastReceivers.remove(broadcastId); iterator.remove(); leAudioService.stopBroadcast(broadcastId); } else { /* Unintentional disconnection of primary device in private broadcast mode */ Loading @@ -3010,7 +3038,7 @@ public class BassClientService extends ProfileService { && (getConnectionState(d) == BluetoothProfile .STATE_CONNECTED))) { mLocalBroadcastReceivers.remove(broadcastId); iterator.remove(); leAudioService.stopBroadcast(broadcastId); continue; } Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +52 −15 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask; import static com.android.bluetooth.flags.Flags.leaudioApiSynchronizedBlockFix; import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState; import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment; import static com.android.bluetooth.flags.Flags.leaudioBroadcastFeatureSupport; import static com.android.bluetooth.flags.Flags.leaudioUseAudioModeListener; import static com.android.modules.utils.build.SdkLevel.isAtLeastU; Loading Loading @@ -196,6 +198,7 @@ public class LeAudioService extends ProfileService { private final LinkedList<BluetoothLeBroadcastSettings> mCreateBroadcastQueue = new LinkedList<>(); boolean mIsSourceStreamMonitorModeEnabled = false; boolean mIsBroadcastPausedFromOutside = false; @VisibleForTesting TbsService mTbsService; Loading Loading @@ -530,6 +533,7 @@ public class LeAudioService extends ProfileService { mCreateBroadcastQueue.clear(); mAwaitingBroadcastCreateResponse = false; mIsSourceStreamMonitorModeEnabled = false; mIsBroadcastPausedFromOutside = false; clearBroadcastTimeoutCallback(); Loading Loading @@ -1262,7 +1266,18 @@ public class LeAudioService extends ProfileService { return; } if (Flags.leaudioBroadcastAssistantPeripheralEntrustment()) { if (leaudioBigDependsOnAudioState()) { if (isPlaying(broadcastId)) { Log.d(TAG, "pauseBroadcast"); mIsBroadcastPausedFromOutside = true; mLeAudioBroadcasterNativeInterface.pauseBroadcast(broadcastId); } else if (isPaused(broadcastId)) { transitionFromBroadcastToUnicast(); } else { Log.d(TAG, "pauseBroadcast: Broadcast is stopped, skip pause request"); } } else { if (leaudioBroadcastAssistantPeripheralEntrustment()) { if (!isPlaying(broadcastId)) { Log.d(TAG, "pauseBroadcast: Broadcast is not playing, skip pause request"); return; Loading @@ -1278,6 +1293,7 @@ public class LeAudioService extends ProfileService { Log.d(TAG, "pauseBroadcast"); mLeAudioBroadcasterNativeInterface.pauseBroadcast(broadcastId); } } /** * Stop LeAudio Broadcast instance. Loading Loading @@ -1345,7 +1361,23 @@ public class LeAudioService extends ProfileService { return false; } return descriptor.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING); return (descriptor.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING)); } /** * Checks if Broadcast instance is paused. * * @param broadcastId broadcast instance identifier * @return true if if broadcast is paused, false otherwise */ public boolean isPaused(int broadcastId) { LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); if (descriptor == null) { Log.e(TAG, "isPaused: No valid descriptor for broadcastId: " + broadcastId); return false; } return (descriptor.mState.equals(LeAudioStackEvent.BROADCAST_STATE_PAUSED)); } /** Loading Loading @@ -3535,13 +3567,18 @@ public class LeAudioService extends ProfileService { broadcastId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST)); if (!Flags.leaudioBroadcastAssistantPeripheralEntrustment()) { if (bassClientService != null) { if (!leaudioBroadcastAssistantPeripheralEntrustment()) { bassClientService.suspendReceiversSourceSynchronization(broadcastId); } else if (leaudioBigDependsOnAudioState()) { bassClientService.cacheSuspendingSources(broadcastId); } } if (!leaudioBigDependsOnAudioState() || mIsBroadcastPausedFromOutside) { mIsBroadcastPausedFromOutside = false; transitionFromBroadcastToUnicast(); } break; case LeAudioStackEvent.BROADCAST_STATE_STOPPING: Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " stopping."); Loading android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java +100 −60 Original line number Diff line number Diff line Loading @@ -3823,6 +3823,105 @@ public class BassClientServiceTest { } } @Test public void testLocalAddSourceWhenBroadcastIsPlaying() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT); doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); if (Flags.leaudioBigDependsOnAudioState()) { doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); } prepareTwoSynchronizedDevicesForLocalBroadcast(); } @Test public void testLocalAddSourceWhenBroadcastIsPaused() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT); mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE); doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); doReturn(true).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); prepareTwoSynchronizedDevicesForLocalBroadcast(); } @Test public void testLocalAddSourceWhenBroadcastIsStopped() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT); doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); if (Flags.leaudioBigDependsOnAudioState()) { doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); } BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID); doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(meta))) .when(mLeAudioService) .getAllBroadcastMetadata(); prepareConnectedDeviceGroup(); mBassClientService.addSource(mCurrentDevice, meta, true); try { verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()) .onSourceAddFailed( eq(mCurrentDevice), eq(meta), eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Test public void testSinksDisconnectionWhenBroadcastIsPlaying() { /* Imitate broadcast being active */ doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); if (Flags.leaudioBigDependsOnAudioState()) { doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); } prepareTwoSynchronizedDevicesForLocalBroadcast(); /* Imitiate scenario when if there would be broadcast - stop would be called */ mBassClientService.handleDeviceDisconnection(mCurrentDevice, true); mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true); verify(mLeAudioService, times(1)).stopBroadcast(eq(TEST_BROADCAST_ID)); } @Test public void testSinksDisconnectionWhenBroadcastIsPaused() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE); /* Imitate broadcast being active */ doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); doReturn(true).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); prepareTwoSynchronizedDevicesForLocalBroadcast(); /* Imitiate scenario when if there would be broadcast - stop would be called */ mBassClientService.handleDeviceDisconnection(mCurrentDevice, true); mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true); verify(mLeAudioService, times(1)).stopBroadcast(eq(TEST_BROADCAST_ID)); } @Test public void testSinksDisconnectionWhenBroadcastIsStopped() { /* Imitate broadcast being active */ doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); if (Flags.leaudioBigDependsOnAudioState()) { doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); } prepareTwoSynchronizedDevicesForLocalBroadcast(); doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); /* Imitiate scenario when if there would be broadcast - stop would be called */ mBassClientService.handleDeviceDisconnection(mCurrentDevice, true); mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true); verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID)); } @Test public void testPrivateBroadcastIntentionalDisconnection() { /* Imitate broadcast being active */ Loading Loading @@ -3944,61 +4043,7 @@ public class BassClientServiceTest { /* Imitate broadcast being active */ doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID); doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(meta))) .when(mLeAudioService) .getAllBroadcastMetadata(); prepareConnectedDeviceGroup(); verifyAddSourceForGroup(meta); for (BassClientStateMachine sm : mStateMachines.values()) { if (sm.getDevice().equals(mCurrentDevice)) { injectRemoteSourceStateSourceAdded( sm, meta, TEST_SOURCE_ID, BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, meta.isEncrypted() ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING : BluetoothLeBroadcastReceiveState .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, null); // verify source id try { verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()) .onSourceAdded( eq(mCurrentDevice), eq(TEST_SOURCE_ID), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } else if (sm.getDevice().equals(mCurrentDevice1)) { injectRemoteSourceStateSourceAdded( sm, meta, TEST_SOURCE_ID + 1, BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, meta.isEncrypted() ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING : BluetoothLeBroadcastReceiveState .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, null); // verify source id try { verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()) .onSourceAdded( eq(mCurrentDevice1), eq(TEST_SOURCE_ID + 1), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /* Imitate broadcast being not active */ doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); prepareTwoSynchronizedDevicesForLocalBroadcast(); mBassClientService.notifyBroadcastStateChanged( 0 /* BROADCAST_STATE_STOPPED */, TEST_BROADCAST_ID); Loading @@ -4007,11 +4052,6 @@ public class BassClientServiceTest { mBassClientService.handleDeviceDisconnection(mCurrentDevice, true); mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true); /* Imitate first device being in disconnected state */ doReturn(BluetoothProfile.STATE_DISCONNECTED) .when(mStateMachines.get(mCurrentDevice)) .getConnectionState(); /* After second device disconnection and de-synchronization expect not calling broadcast to * stop due to previous broadcast stream stopped */ verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID)); Loading android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java +237 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes system/bta/le_audio/broadcaster/broadcaster.cc +39 −24 Original line number Diff line number Diff line Loading @@ -708,8 +708,12 @@ public: log::info("broadcast_id={}", broadcast_id); if (broadcasts_.count(broadcast_id) != 0) { if (!com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { log::info("Stopping AudioHalClient"); if (le_audio_source_hal_client_) le_audio_source_hal_client_->Stop(); if (le_audio_source_hal_client_) { le_audio_source_hal_client_->Stop(); } } audio_state_ = AudioState::SUSPENDED; broadcasts_[broadcast_id]->SetMuted(true); broadcasts_[broadcast_id]->ProcessMessage( Loading Loading @@ -927,11 +931,12 @@ public: "assert failed: broadcasts_.count(broadcast_id) != 0"); broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT, evt); auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( if (!com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), false); log::assert_that(result, "Could not update session in codec manager"); le_audio_source_hal_client_.reset(); } } break; default: log::error("Invalid event={}", event); Loading Loading @@ -1052,13 +1057,13 @@ public: broadcast->SetMuted(false); auto is_started = instance->le_audio_source_hal_client_->Start( broadcast_config.GetAudioHalClientConfig(), &audio_receiver_); if (!com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { if (!is_started) { /* Audio Source setup failed - stop the broadcast */ instance->StopAudioBroadcast(broadcast_id); return; } if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { } else { instance->UpdateAudioActiveStateInPublicAnnouncement(); } } Loading Loading @@ -1324,15 +1329,20 @@ public: virtual void OnAudioSuspend(void) override { log::info(""); /* TODO: Should we suspend all broadcasts - remove BIGs? */ if (!instance) { return; } instance->audio_state_ = AudioState::SUSPENDED; if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->UpdateAudioActiveStateInPublicAnnouncement(); // TODO: add some timeout to execute below for (auto& broadcast_pair : instance->broadcasts_) { auto& broadcast = broadcast_pair.second; broadcast->SetMuted(true); broadcast->ProcessMessage(BroadcastStateMachine::Message::SUSPEND, nullptr); } } } Loading @@ -1340,18 +1350,23 @@ public: log::info(""); if (!instance) return; /* TODO: Should we resume all broadcasts - recreate BIGs? */ instance->audio_state_ = AudioState::ACTIVE; if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->UpdateAudioActiveStateInPublicAnnouncement(); for (auto& broadcast_pair : instance->broadcasts_) { auto& broadcast = broadcast_pair.second; broadcast->ProcessMessage(BroadcastStateMachine::Message::START, nullptr); } instance->le_audio_source_hal_client_->ConfirmStreamingRequest(); } else { if (!IsAnyoneStreaming()) { instance->le_audio_source_hal_client_->CancelStreamingRequest(); return; } instance->le_audio_source_hal_client_->ConfirmStreamingRequest(); if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->UpdateAudioActiveStateInPublicAnnouncement(); } } Loading Loading
android/app/src/com/android/bluetooth/bass_client/BassClientService.java +45 −17 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask; import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState; import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment; import static com.android.bluetooth.flags.Flags.leaudioBroadcastAudioHandoverPolicies; import static com.android.bluetooth.flags.Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine; Loading Loading @@ -2424,6 +2425,20 @@ public class BassClientService extends ProfileService { if (leaudioBroadcastAssistantPeripheralEntrustment()) { if (isLocalBroadcast(sourceMetadata)) { LeAudioService leAudioService = mServiceFactory.getLeAudioService(); if (leaudioBigDependsOnAudioState()) { if (leAudioService == null || !(leAudioService.isPaused(sourceMetadata.getBroadcastId()) || leAudioService.isPlaying(sourceMetadata.getBroadcastId()))) { Log.w(TAG, "addSource: Local source can't be add"); mCallbacks.notifySourceAddFailed( sink, sourceMetadata, BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES); return; } } else { if (leAudioService == null || !leAudioService.isPlaying(sourceMetadata.getBroadcastId())) { Log.w(TAG, "addSource: Local source can't be add"); Loading @@ -2436,6 +2451,7 @@ public class BassClientService extends ProfileService { return; } } } } else { if (!isAllowedToAddSource()) { Log.d(TAG, "Add source to pending list"); Loading Loading @@ -2963,17 +2979,29 @@ public class BassClientService extends ProfileService { return; } for (Map.Entry<Integer, HashSet<BluetoothDevice>> entry : mLocalBroadcastReceivers.entrySet()) { Iterator<Map.Entry<Integer, HashSet<BluetoothDevice>>> iterator = mLocalBroadcastReceivers.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Integer, HashSet<BluetoothDevice>> entry = iterator.next(); Integer broadcastId = entry.getKey(); HashSet<BluetoothDevice> devices = entry.getValue(); if (leaudioBigDependsOnAudioState()) { /* If somehow there is a non configured/playing broadcast, let's remove it */ if (!(leAudioService.isPaused(broadcastId) || leAudioService.isPlaying(broadcastId))) { Log.w(TAG, "Non playing broadcast remove from receivers list"); iterator.remove(); continue; } } else { /* If somehow there is a non playing broadcast, let's remove it */ if (!leAudioService.isPlaying(broadcastId)) { Log.w(TAG, "Non playing broadcast remove from receivers list"); mLocalBroadcastReceivers.remove(broadcastId); iterator.remove(); continue; } } if (isIntentional) { /* Check if disconnecting device participated in this broadcast reception */ Loading @@ -2998,7 +3026,7 @@ public class BassClientService extends ProfileService { + "(broadcast ID: " + broadcastId + ") receivers - stopping broadcast"); mLocalBroadcastReceivers.remove(broadcastId); iterator.remove(); leAudioService.stopBroadcast(broadcastId); } else { /* Unintentional disconnection of primary device in private broadcast mode */ Loading @@ -3010,7 +3038,7 @@ public class BassClientService extends ProfileService { && (getConnectionState(d) == BluetoothProfile .STATE_CONNECTED))) { mLocalBroadcastReceivers.remove(broadcastId); iterator.remove(); leAudioService.stopBroadcast(broadcastId); continue; } Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +52 −15 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask; import static com.android.bluetooth.flags.Flags.leaudioApiSynchronizedBlockFix; import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState; import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment; import static com.android.bluetooth.flags.Flags.leaudioBroadcastFeatureSupport; import static com.android.bluetooth.flags.Flags.leaudioUseAudioModeListener; import static com.android.modules.utils.build.SdkLevel.isAtLeastU; Loading Loading @@ -196,6 +198,7 @@ public class LeAudioService extends ProfileService { private final LinkedList<BluetoothLeBroadcastSettings> mCreateBroadcastQueue = new LinkedList<>(); boolean mIsSourceStreamMonitorModeEnabled = false; boolean mIsBroadcastPausedFromOutside = false; @VisibleForTesting TbsService mTbsService; Loading Loading @@ -530,6 +533,7 @@ public class LeAudioService extends ProfileService { mCreateBroadcastQueue.clear(); mAwaitingBroadcastCreateResponse = false; mIsSourceStreamMonitorModeEnabled = false; mIsBroadcastPausedFromOutside = false; clearBroadcastTimeoutCallback(); Loading Loading @@ -1262,7 +1266,18 @@ public class LeAudioService extends ProfileService { return; } if (Flags.leaudioBroadcastAssistantPeripheralEntrustment()) { if (leaudioBigDependsOnAudioState()) { if (isPlaying(broadcastId)) { Log.d(TAG, "pauseBroadcast"); mIsBroadcastPausedFromOutside = true; mLeAudioBroadcasterNativeInterface.pauseBroadcast(broadcastId); } else if (isPaused(broadcastId)) { transitionFromBroadcastToUnicast(); } else { Log.d(TAG, "pauseBroadcast: Broadcast is stopped, skip pause request"); } } else { if (leaudioBroadcastAssistantPeripheralEntrustment()) { if (!isPlaying(broadcastId)) { Log.d(TAG, "pauseBroadcast: Broadcast is not playing, skip pause request"); return; Loading @@ -1278,6 +1293,7 @@ public class LeAudioService extends ProfileService { Log.d(TAG, "pauseBroadcast"); mLeAudioBroadcasterNativeInterface.pauseBroadcast(broadcastId); } } /** * Stop LeAudio Broadcast instance. Loading Loading @@ -1345,7 +1361,23 @@ public class LeAudioService extends ProfileService { return false; } return descriptor.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING); return (descriptor.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING)); } /** * Checks if Broadcast instance is paused. * * @param broadcastId broadcast instance identifier * @return true if if broadcast is paused, false otherwise */ public boolean isPaused(int broadcastId) { LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); if (descriptor == null) { Log.e(TAG, "isPaused: No valid descriptor for broadcastId: " + broadcastId); return false; } return (descriptor.mState.equals(LeAudioStackEvent.BROADCAST_STATE_PAUSED)); } /** Loading Loading @@ -3535,13 +3567,18 @@ public class LeAudioService extends ProfileService { broadcastId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST)); if (!Flags.leaudioBroadcastAssistantPeripheralEntrustment()) { if (bassClientService != null) { if (!leaudioBroadcastAssistantPeripheralEntrustment()) { bassClientService.suspendReceiversSourceSynchronization(broadcastId); } else if (leaudioBigDependsOnAudioState()) { bassClientService.cacheSuspendingSources(broadcastId); } } if (!leaudioBigDependsOnAudioState() || mIsBroadcastPausedFromOutside) { mIsBroadcastPausedFromOutside = false; transitionFromBroadcastToUnicast(); } break; case LeAudioStackEvent.BROADCAST_STATE_STOPPING: Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " stopping."); Loading
android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java +100 −60 Original line number Diff line number Diff line Loading @@ -3823,6 +3823,105 @@ public class BassClientServiceTest { } } @Test public void testLocalAddSourceWhenBroadcastIsPlaying() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT); doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); if (Flags.leaudioBigDependsOnAudioState()) { doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); } prepareTwoSynchronizedDevicesForLocalBroadcast(); } @Test public void testLocalAddSourceWhenBroadcastIsPaused() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT); mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE); doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); doReturn(true).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); prepareTwoSynchronizedDevicesForLocalBroadcast(); } @Test public void testLocalAddSourceWhenBroadcastIsStopped() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT); doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); if (Flags.leaudioBigDependsOnAudioState()) { doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); } BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID); doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(meta))) .when(mLeAudioService) .getAllBroadcastMetadata(); prepareConnectedDeviceGroup(); mBassClientService.addSource(mCurrentDevice, meta, true); try { verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()) .onSourceAddFailed( eq(mCurrentDevice), eq(meta), eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Test public void testSinksDisconnectionWhenBroadcastIsPlaying() { /* Imitate broadcast being active */ doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); if (Flags.leaudioBigDependsOnAudioState()) { doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); } prepareTwoSynchronizedDevicesForLocalBroadcast(); /* Imitiate scenario when if there would be broadcast - stop would be called */ mBassClientService.handleDeviceDisconnection(mCurrentDevice, true); mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true); verify(mLeAudioService, times(1)).stopBroadcast(eq(TEST_BROADCAST_ID)); } @Test public void testSinksDisconnectionWhenBroadcastIsPaused() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE); /* Imitate broadcast being active */ doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); doReturn(true).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); prepareTwoSynchronizedDevicesForLocalBroadcast(); /* Imitiate scenario when if there would be broadcast - stop would be called */ mBassClientService.handleDeviceDisconnection(mCurrentDevice, true); mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true); verify(mLeAudioService, times(1)).stopBroadcast(eq(TEST_BROADCAST_ID)); } @Test public void testSinksDisconnectionWhenBroadcastIsStopped() { /* Imitate broadcast being active */ doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); if (Flags.leaudioBigDependsOnAudioState()) { doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID); } prepareTwoSynchronizedDevicesForLocalBroadcast(); doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); /* Imitiate scenario when if there would be broadcast - stop would be called */ mBassClientService.handleDeviceDisconnection(mCurrentDevice, true); mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true); verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID)); } @Test public void testPrivateBroadcastIntentionalDisconnection() { /* Imitate broadcast being active */ Loading Loading @@ -3944,61 +4043,7 @@ public class BassClientServiceTest { /* Imitate broadcast being active */ doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID); doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(meta))) .when(mLeAudioService) .getAllBroadcastMetadata(); prepareConnectedDeviceGroup(); verifyAddSourceForGroup(meta); for (BassClientStateMachine sm : mStateMachines.values()) { if (sm.getDevice().equals(mCurrentDevice)) { injectRemoteSourceStateSourceAdded( sm, meta, TEST_SOURCE_ID, BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, meta.isEncrypted() ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING : BluetoothLeBroadcastReceiveState .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, null); // verify source id try { verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()) .onSourceAdded( eq(mCurrentDevice), eq(TEST_SOURCE_ID), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } else if (sm.getDevice().equals(mCurrentDevice1)) { injectRemoteSourceStateSourceAdded( sm, meta, TEST_SOURCE_ID + 1, BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, meta.isEncrypted() ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING : BluetoothLeBroadcastReceiveState .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, null); // verify source id try { verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()) .onSourceAdded( eq(mCurrentDevice1), eq(TEST_SOURCE_ID + 1), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /* Imitate broadcast being not active */ doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID); prepareTwoSynchronizedDevicesForLocalBroadcast(); mBassClientService.notifyBroadcastStateChanged( 0 /* BROADCAST_STATE_STOPPED */, TEST_BROADCAST_ID); Loading @@ -4007,11 +4052,6 @@ public class BassClientServiceTest { mBassClientService.handleDeviceDisconnection(mCurrentDevice, true); mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true); /* Imitate first device being in disconnected state */ doReturn(BluetoothProfile.STATE_DISCONNECTED) .when(mStateMachines.get(mCurrentDevice)) .getConnectionState(); /* After second device disconnection and de-synchronization expect not calling broadcast to * stop due to previous broadcast stream stopped */ verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID)); Loading
android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java +237 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes
system/bta/le_audio/broadcaster/broadcaster.cc +39 −24 Original line number Diff line number Diff line Loading @@ -708,8 +708,12 @@ public: log::info("broadcast_id={}", broadcast_id); if (broadcasts_.count(broadcast_id) != 0) { if (!com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { log::info("Stopping AudioHalClient"); if (le_audio_source_hal_client_) le_audio_source_hal_client_->Stop(); if (le_audio_source_hal_client_) { le_audio_source_hal_client_->Stop(); } } audio_state_ = AudioState::SUSPENDED; broadcasts_[broadcast_id]->SetMuted(true); broadcasts_[broadcast_id]->ProcessMessage( Loading Loading @@ -927,11 +931,12 @@ public: "assert failed: broadcasts_.count(broadcast_id) != 0"); broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT, evt); auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( if (!com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), false); log::assert_that(result, "Could not update session in codec manager"); le_audio_source_hal_client_.reset(); } } break; default: log::error("Invalid event={}", event); Loading Loading @@ -1052,13 +1057,13 @@ public: broadcast->SetMuted(false); auto is_started = instance->le_audio_source_hal_client_->Start( broadcast_config.GetAudioHalClientConfig(), &audio_receiver_); if (!com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { if (!is_started) { /* Audio Source setup failed - stop the broadcast */ instance->StopAudioBroadcast(broadcast_id); return; } if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { } else { instance->UpdateAudioActiveStateInPublicAnnouncement(); } } Loading Loading @@ -1324,15 +1329,20 @@ public: virtual void OnAudioSuspend(void) override { log::info(""); /* TODO: Should we suspend all broadcasts - remove BIGs? */ if (!instance) { return; } instance->audio_state_ = AudioState::SUSPENDED; if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->UpdateAudioActiveStateInPublicAnnouncement(); // TODO: add some timeout to execute below for (auto& broadcast_pair : instance->broadcasts_) { auto& broadcast = broadcast_pair.second; broadcast->SetMuted(true); broadcast->ProcessMessage(BroadcastStateMachine::Message::SUSPEND, nullptr); } } } Loading @@ -1340,18 +1350,23 @@ public: log::info(""); if (!instance) return; /* TODO: Should we resume all broadcasts - recreate BIGs? */ instance->audio_state_ = AudioState::ACTIVE; if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->UpdateAudioActiveStateInPublicAnnouncement(); for (auto& broadcast_pair : instance->broadcasts_) { auto& broadcast = broadcast_pair.second; broadcast->ProcessMessage(BroadcastStateMachine::Message::START, nullptr); } instance->le_audio_source_hal_client_->ConfirmStreamingRequest(); } else { if (!IsAnyoneStreaming()) { instance->le_audio_source_hal_client_->CancelStreamingRequest(); return; } instance->le_audio_source_hal_client_->ConfirmStreamingRequest(); if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->UpdateAudioActiveStateInPublicAnnouncement(); } } Loading