Loading packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt +26 −9 Original line number Diff line number Diff line Loading @@ -30,9 +30,11 @@ import com.android.settingslib.bluetooth.BluetoothUtils import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.settingslib.bluetooth.onBroadcastStartedOrStopped import com.android.settingslib.bluetooth.onBroadcastToUnicastFallbackGroupChanged import com.android.settingslib.bluetooth.onProfileConnectionStateChanged import com.android.settingslib.bluetooth.onServiceStateChanged import com.android.settingslib.bluetooth.onSourceConnectedOrRemoved import com.android.settingslib.flags.Flags import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MAX import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MIN import com.android.settingslib.volume.shared.AudioSharingLogger Loading Loading @@ -140,11 +142,22 @@ class AudioSharingRepositoryImpl( } override val primaryGroupId: StateFlow<Int> = if (Flags.adoptPrimaryGroupManagementApiV2()) { isAudioSharingProfilesReady.flatMapLatest { ready -> if (ready) { btManager.profileManager.leAudioProfile.onBroadcastToUnicastFallbackGroupChanged .onStart { emit(BluetoothCsipSetCoordinator.GROUP_ID_INVALID) } .flowOn(backgroundCoroutineContext) } else { flowOf(BluetoothCsipSetCoordinator.GROUP_ID_INVALID) } } } else { primaryChange .map { BluetoothUtils.getPrimaryGroupIdForBroadcast(contentResolver) } .onStart { emit(BluetoothUtils.getPrimaryGroupIdForBroadcast(contentResolver)) } .flowOn(backgroundCoroutineContext) .stateIn( }.stateIn( coroutineScope, SharingStarted.WhileSubscribed(), BluetoothCsipSetCoordinator.GROUP_ID_INVALID Loading Loading @@ -273,8 +286,12 @@ class AudioSharingRepositoryImpl( private fun isVolumeControlProfileReady(): Boolean = btManager.profileManager.volumeControlProfile?.isProfileReady ?: false private fun isLeAudioProfileReady(): Boolean = btManager.profileManager.leAudioProfile?.isProfileReady ?: false private fun isAudioSharingProfilesReady(): Boolean = isBroadcastProfileReady() && isAssistantProfileReady() && isVolumeControlProfileReady() && isLeAudioProfileReady() private fun isBroadcasting(): Boolean = btManager.profileManager.leAudioBroadcastProfile?.isEnabled(null) ?: false Loading packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt +189 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.settingslib.volume.data.repository import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothLeAudio import android.bluetooth.BluetoothLeBroadcast import android.bluetooth.BluetoothLeBroadcastAssistant import android.bluetooth.BluetoothLeBroadcastReceiveState Loading @@ -26,6 +27,8 @@ import android.bluetooth.BluetoothVolumeControl import android.content.ContentResolver import android.content.Context import android.database.ContentObserver import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.provider.Settings import androidx.test.core.app.ApplicationProvider Loading @@ -36,11 +39,13 @@ import com.android.settingslib.bluetooth.BluetoothEventManager import com.android.settingslib.bluetooth.BluetoothUtils import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager import com.android.settingslib.bluetooth.LeAudioProfile import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.settingslib.bluetooth.LocalBluetoothProfileManager import com.android.settingslib.bluetooth.VolumeControlProfile import com.android.settingslib.flags.Flags import com.google.common.truth.Truth import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn Loading Loading @@ -83,6 +88,8 @@ class AudioSharingRepositoryTest { @Mock private lateinit var volumeControl: VolumeControlProfile @Mock private lateinit var leAudio: LeAudioProfile @Mock private lateinit var eventManager: BluetoothEventManager @Mock private lateinit var deviceManager: CachedBluetoothDeviceManager Loading @@ -104,6 +111,8 @@ class AudioSharingRepositoryTest { private lateinit var assistantCallbackCaptor: ArgumentCaptor<BluetoothLeBroadcastAssistant.Callback> @Captor private lateinit var leAudioCallbackCaptor: ArgumentCaptor<BluetoothLeAudio.Callback> @Captor private lateinit var btCallbackCaptor: ArgumentCaptor<BluetoothCallback> @Captor private lateinit var contentObserverCaptor: ArgumentCaptor<ContentObserver> Loading @@ -123,6 +132,7 @@ class AudioSharingRepositoryTest { `when`(profileManager.leAudioBroadcastProfile).thenReturn(broadcast) `when`(profileManager.leAudioBroadcastAssistantProfile).thenReturn(assistant) `when`(profileManager.volumeControlProfile).thenReturn(volumeControl) `when`(profileManager.leAudioProfile).thenReturn(leAudio) `when`(btManager.eventManager).thenReturn(eventManager) `when`(btManager.cachedDeviceManager).thenReturn(deviceManager) `when`(broadcast.isEnabled(null)).thenReturn(true) Loading Loading @@ -160,6 +170,7 @@ class AudioSharingRepositoryTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val states = mutableListOf<Boolean?>() underTest.inAudioSharing.onEach { states.add(it) }.launchIn(backgroundScope) runCurrent() Loading Loading @@ -192,7 +203,8 @@ class AudioSharingRepositoryTest { } @Test fun primaryGroupIdChange_emitValues() { @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun primaryGroupIdChange_getValueFromContentResolver_emitValues() { testScope.runTest { val groupIds = mutableListOf<Int?>() underTest.primaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope) Loading @@ -209,6 +221,29 @@ class AudioSharingRepositoryTest { } @Test @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun primaryGroupIdChange_getValueFromApi_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val groupIds = mutableListOf<Int?>() underTest.primaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope) runCurrent() triggerPrimaryGroupIdChanged() runCurrent() Truth.assertThat(groupIds) .containsExactly( TEST_GROUP_ID_INVALID, TEST_GROUP_ID2 ) } } @Test @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun primaryDeviceChange_emitValues() { testScope.runTest { `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2)) Loading @@ -223,6 +258,26 @@ class AudioSharingRepositoryTest { } } @Test @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun primaryDeviceChange_getPrimaryGroupIdFromApi_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2)) val devices = mutableListOf<CachedBluetoothDevice?>() underTest.primaryDevice.onEach { devices.add(it) }.launchIn(backgroundScope) runCurrent() triggerPrimaryGroupIdChanged() runCurrent() Truth.assertThat(devices).containsExactly(null, cachedDevice2) } } @Test fun secondaryGroupIdChange_profileNotReady_assistantCallbackNotRegistered() { testScope.runTest { Loading @@ -234,11 +289,13 @@ class AudioSharingRepositoryTest { } @Test @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun secondaryGroupIdChange_profileReady_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val groupIds = mutableListOf<Int?>() underTest.secondaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope) runCurrent() Loading Loading @@ -284,11 +341,65 @@ class AudioSharingRepositoryTest { } @Test @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun secondaryGroupIdChange_getPrimaryGroupIdFromApi_profileReady_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val groupIds = mutableListOf<Int?>() underTest.secondaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope) runCurrent() triggerSourceAddedWithGetPrimaryGroupApi() runCurrent() triggerPrimaryGroupIdChanged() runCurrent() triggerSourceRemovedWithGetPrimaryGroupApi() runCurrent() triggerSourceAddedWithGetPrimaryGroupApi() runCurrent() triggerProfileConnectionChangeWithGetPrimaryGroupApi( BluetoothAdapter.STATE_CONNECTING, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT ) runCurrent() triggerProfileConnectionChangeWithGetPrimaryGroupApi( BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO ) runCurrent() triggerProfileConnectionChangeWithGetPrimaryGroupApi( BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT ) runCurrent() Truth.assertThat(groupIds) .containsExactly( TEST_GROUP_ID_INVALID, TEST_GROUP_ID2, TEST_GROUP_ID1, TEST_GROUP_ID_INVALID, TEST_GROUP_ID2, TEST_GROUP_ID_INVALID ) Truth.assertThat(logger.logs) .containsAtLeastElementsIn( listOf( "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID_INVALID", "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID2", "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID1", ) ).inOrder() } } @Test @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun secondaryDeviceChange_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val devices = mutableListOf<CachedBluetoothDevice?>() underTest.secondaryDevice.onEach { devices.add(it) }.launchIn(backgroundScope) runCurrent() Loading @@ -306,12 +417,38 @@ class AudioSharingRepositoryTest { } } @Test @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun secondaryDeviceChange_getPrimaryGroupIdFromApi_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val devices = mutableListOf<CachedBluetoothDevice?>() underTest.secondaryDevice.onEach { devices.add(it) }.launchIn(backgroundScope) runCurrent() triggerSourceAddedWithGetPrimaryGroupApi() runCurrent() triggerPrimaryGroupIdChanged() runCurrent() Truth.assertThat(devices) .containsExactly( null, cachedDevice2, cachedDevice1, ) } } @Test fun volumeMapChange_profileReady_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val volumeMaps = mutableListOf<GroupIdToVolumes?>() underTest.volumeMap.onEach { volumeMaps.add(it) }.launchIn(backgroundScope) runCurrent() Loading Loading @@ -354,6 +491,7 @@ class AudioSharingRepositoryTest { } @Test @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun setSecondaryVolume_setValue() { testScope.runTest { Settings.Secure.putInt( Loading @@ -375,6 +513,29 @@ class AudioSharingRepositoryTest { } } @Test @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun setSecondaryVolume_getPrimaryGroupIdFromApi_setValue() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) `when`(leAudio.broadcastToUnicastFallbackGroup).thenReturn(TEST_GROUP_ID2) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2)) underTest.setSecondaryVolume(TEST_VOLUME1) runCurrent() verify(volumeControl).setDeviceVolume(device1, TEST_VOLUME1, true) Truth.assertThat(logger.logs) .isEqualTo( listOf( "onSetVolumeRequested volume=$TEST_VOLUME1", ) ) } } private fun triggerAudioSharingStateChange( type: TriggerType, broadcastAction: BluetoothLeBroadcast.Callback.() -> Unit Loading Loading @@ -404,6 +565,13 @@ class AudioSharingRepositoryTest { assistantCallbackCaptor.value.sourceAdded(device1) } private fun triggerSourceAddedWithGetPrimaryGroupApi() { verify(assistant).registerServiceCallBack(any(), assistantCallbackCaptor.capture()) `when`(leAudio.broadcastToUnicastFallbackGroup).thenReturn(TEST_GROUP_ID1) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2)) assistantCallbackCaptor.value.sourceAdded(device1) } private fun triggerSourceRemoved() { verify(assistant).registerServiceCallBack(any(), assistantCallbackCaptor.capture()) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1)) Loading @@ -415,6 +583,13 @@ class AudioSharingRepositoryTest { assistantCallbackCaptor.value.sourceRemoved(device2) } private fun triggerSourceRemovedWithGetPrimaryGroupApi() { verify(assistant).registerServiceCallBack(any(), assistantCallbackCaptor.capture()) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1)) `when`(leAudio.broadcastToUnicastFallbackGroup).thenReturn(TEST_GROUP_ID1) assistantCallbackCaptor.value.sourceRemoved(device2) } private fun triggerProfileConnectionChange(state: Int, profile: Int) { verify(eventManager).registerCallback(btCallbackCaptor.capture()) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1)) Loading @@ -426,6 +601,13 @@ class AudioSharingRepositoryTest { btCallbackCaptor.value.onProfileConnectionStateChanged(cachedDevice2, state, profile) } private fun triggerProfileConnectionChangeWithGetPrimaryGroupApi(state: Int, profile: Int) { verify(eventManager).registerCallback(btCallbackCaptor.capture()) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1)) `when`(leAudio.broadcastToUnicastFallbackGroup).thenReturn(TEST_GROUP_ID1) btCallbackCaptor.value.onProfileConnectionStateChanged(cachedDevice2, state, profile) } private fun triggerContentObserverChange() { verify(contentResolver) .registerContentObserver( Loading @@ -442,6 +624,12 @@ class AudioSharingRepositoryTest { contentObserverCaptor.value.primaryChanged() } private fun triggerPrimaryGroupIdChanged() { verify(leAudio).registerCallback(any(), leAudioCallbackCaptor.capture()) `when`(leAudio.broadcastToUnicastFallbackGroup).thenReturn(TEST_GROUP_ID2) leAudioCallbackCaptor.value.onBroadcastToUnicastFallbackGroupChanged(TEST_GROUP_ID2) } private fun triggerVolumeMapChange(change: Pair<BluetoothDevice, Int>) { verify(volumeControl).registerCallback(any(), volumeCallbackCaptor.capture()) volumeCallbackCaptor.value.onDeviceVolumeChanged(change.first, change.second) Loading Loading
packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt +26 −9 Original line number Diff line number Diff line Loading @@ -30,9 +30,11 @@ import com.android.settingslib.bluetooth.BluetoothUtils import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.settingslib.bluetooth.onBroadcastStartedOrStopped import com.android.settingslib.bluetooth.onBroadcastToUnicastFallbackGroupChanged import com.android.settingslib.bluetooth.onProfileConnectionStateChanged import com.android.settingslib.bluetooth.onServiceStateChanged import com.android.settingslib.bluetooth.onSourceConnectedOrRemoved import com.android.settingslib.flags.Flags import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MAX import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MIN import com.android.settingslib.volume.shared.AudioSharingLogger Loading Loading @@ -140,11 +142,22 @@ class AudioSharingRepositoryImpl( } override val primaryGroupId: StateFlow<Int> = if (Flags.adoptPrimaryGroupManagementApiV2()) { isAudioSharingProfilesReady.flatMapLatest { ready -> if (ready) { btManager.profileManager.leAudioProfile.onBroadcastToUnicastFallbackGroupChanged .onStart { emit(BluetoothCsipSetCoordinator.GROUP_ID_INVALID) } .flowOn(backgroundCoroutineContext) } else { flowOf(BluetoothCsipSetCoordinator.GROUP_ID_INVALID) } } } else { primaryChange .map { BluetoothUtils.getPrimaryGroupIdForBroadcast(contentResolver) } .onStart { emit(BluetoothUtils.getPrimaryGroupIdForBroadcast(contentResolver)) } .flowOn(backgroundCoroutineContext) .stateIn( }.stateIn( coroutineScope, SharingStarted.WhileSubscribed(), BluetoothCsipSetCoordinator.GROUP_ID_INVALID Loading Loading @@ -273,8 +286,12 @@ class AudioSharingRepositoryImpl( private fun isVolumeControlProfileReady(): Boolean = btManager.profileManager.volumeControlProfile?.isProfileReady ?: false private fun isLeAudioProfileReady(): Boolean = btManager.profileManager.leAudioProfile?.isProfileReady ?: false private fun isAudioSharingProfilesReady(): Boolean = isBroadcastProfileReady() && isAssistantProfileReady() && isVolumeControlProfileReady() && isLeAudioProfileReady() private fun isBroadcasting(): Boolean = btManager.profileManager.leAudioBroadcastProfile?.isEnabled(null) ?: false Loading
packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt +189 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.settingslib.volume.data.repository import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothLeAudio import android.bluetooth.BluetoothLeBroadcast import android.bluetooth.BluetoothLeBroadcastAssistant import android.bluetooth.BluetoothLeBroadcastReceiveState Loading @@ -26,6 +27,8 @@ import android.bluetooth.BluetoothVolumeControl import android.content.ContentResolver import android.content.Context import android.database.ContentObserver import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.provider.Settings import androidx.test.core.app.ApplicationProvider Loading @@ -36,11 +39,13 @@ import com.android.settingslib.bluetooth.BluetoothEventManager import com.android.settingslib.bluetooth.BluetoothUtils import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager import com.android.settingslib.bluetooth.LeAudioProfile import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.settingslib.bluetooth.LocalBluetoothProfileManager import com.android.settingslib.bluetooth.VolumeControlProfile import com.android.settingslib.flags.Flags import com.google.common.truth.Truth import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn Loading Loading @@ -83,6 +88,8 @@ class AudioSharingRepositoryTest { @Mock private lateinit var volumeControl: VolumeControlProfile @Mock private lateinit var leAudio: LeAudioProfile @Mock private lateinit var eventManager: BluetoothEventManager @Mock private lateinit var deviceManager: CachedBluetoothDeviceManager Loading @@ -104,6 +111,8 @@ class AudioSharingRepositoryTest { private lateinit var assistantCallbackCaptor: ArgumentCaptor<BluetoothLeBroadcastAssistant.Callback> @Captor private lateinit var leAudioCallbackCaptor: ArgumentCaptor<BluetoothLeAudio.Callback> @Captor private lateinit var btCallbackCaptor: ArgumentCaptor<BluetoothCallback> @Captor private lateinit var contentObserverCaptor: ArgumentCaptor<ContentObserver> Loading @@ -123,6 +132,7 @@ class AudioSharingRepositoryTest { `when`(profileManager.leAudioBroadcastProfile).thenReturn(broadcast) `when`(profileManager.leAudioBroadcastAssistantProfile).thenReturn(assistant) `when`(profileManager.volumeControlProfile).thenReturn(volumeControl) `when`(profileManager.leAudioProfile).thenReturn(leAudio) `when`(btManager.eventManager).thenReturn(eventManager) `when`(btManager.cachedDeviceManager).thenReturn(deviceManager) `when`(broadcast.isEnabled(null)).thenReturn(true) Loading Loading @@ -160,6 +170,7 @@ class AudioSharingRepositoryTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val states = mutableListOf<Boolean?>() underTest.inAudioSharing.onEach { states.add(it) }.launchIn(backgroundScope) runCurrent() Loading Loading @@ -192,7 +203,8 @@ class AudioSharingRepositoryTest { } @Test fun primaryGroupIdChange_emitValues() { @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun primaryGroupIdChange_getValueFromContentResolver_emitValues() { testScope.runTest { val groupIds = mutableListOf<Int?>() underTest.primaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope) Loading @@ -209,6 +221,29 @@ class AudioSharingRepositoryTest { } @Test @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun primaryGroupIdChange_getValueFromApi_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val groupIds = mutableListOf<Int?>() underTest.primaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope) runCurrent() triggerPrimaryGroupIdChanged() runCurrent() Truth.assertThat(groupIds) .containsExactly( TEST_GROUP_ID_INVALID, TEST_GROUP_ID2 ) } } @Test @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun primaryDeviceChange_emitValues() { testScope.runTest { `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2)) Loading @@ -223,6 +258,26 @@ class AudioSharingRepositoryTest { } } @Test @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun primaryDeviceChange_getPrimaryGroupIdFromApi_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2)) val devices = mutableListOf<CachedBluetoothDevice?>() underTest.primaryDevice.onEach { devices.add(it) }.launchIn(backgroundScope) runCurrent() triggerPrimaryGroupIdChanged() runCurrent() Truth.assertThat(devices).containsExactly(null, cachedDevice2) } } @Test fun secondaryGroupIdChange_profileNotReady_assistantCallbackNotRegistered() { testScope.runTest { Loading @@ -234,11 +289,13 @@ class AudioSharingRepositoryTest { } @Test @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun secondaryGroupIdChange_profileReady_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val groupIds = mutableListOf<Int?>() underTest.secondaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope) runCurrent() Loading Loading @@ -284,11 +341,65 @@ class AudioSharingRepositoryTest { } @Test @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun secondaryGroupIdChange_getPrimaryGroupIdFromApi_profileReady_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val groupIds = mutableListOf<Int?>() underTest.secondaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope) runCurrent() triggerSourceAddedWithGetPrimaryGroupApi() runCurrent() triggerPrimaryGroupIdChanged() runCurrent() triggerSourceRemovedWithGetPrimaryGroupApi() runCurrent() triggerSourceAddedWithGetPrimaryGroupApi() runCurrent() triggerProfileConnectionChangeWithGetPrimaryGroupApi( BluetoothAdapter.STATE_CONNECTING, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT ) runCurrent() triggerProfileConnectionChangeWithGetPrimaryGroupApi( BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO ) runCurrent() triggerProfileConnectionChangeWithGetPrimaryGroupApi( BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT ) runCurrent() Truth.assertThat(groupIds) .containsExactly( TEST_GROUP_ID_INVALID, TEST_GROUP_ID2, TEST_GROUP_ID1, TEST_GROUP_ID_INVALID, TEST_GROUP_ID2, TEST_GROUP_ID_INVALID ) Truth.assertThat(logger.logs) .containsAtLeastElementsIn( listOf( "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID_INVALID", "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID2", "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID1", ) ).inOrder() } } @Test @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun secondaryDeviceChange_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val devices = mutableListOf<CachedBluetoothDevice?>() underTest.secondaryDevice.onEach { devices.add(it) }.launchIn(backgroundScope) runCurrent() Loading @@ -306,12 +417,38 @@ class AudioSharingRepositoryTest { } } @Test @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun secondaryDeviceChange_getPrimaryGroupIdFromApi_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val devices = mutableListOf<CachedBluetoothDevice?>() underTest.secondaryDevice.onEach { devices.add(it) }.launchIn(backgroundScope) runCurrent() triggerSourceAddedWithGetPrimaryGroupApi() runCurrent() triggerPrimaryGroupIdChanged() runCurrent() Truth.assertThat(devices) .containsExactly( null, cachedDevice2, cachedDevice1, ) } } @Test fun volumeMapChange_profileReady_emitValues() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) val volumeMaps = mutableListOf<GroupIdToVolumes?>() underTest.volumeMap.onEach { volumeMaps.add(it) }.launchIn(backgroundScope) runCurrent() Loading Loading @@ -354,6 +491,7 @@ class AudioSharingRepositoryTest { } @Test @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun setSecondaryVolume_setValue() { testScope.runTest { Settings.Secure.putInt( Loading @@ -375,6 +513,29 @@ class AudioSharingRepositoryTest { } } @Test @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) fun setSecondaryVolume_getPrimaryGroupIdFromApi_setValue() { testScope.runTest { `when`(broadcast.isProfileReady).thenReturn(true) `when`(assistant.isProfileReady).thenReturn(true) `when`(volumeControl.isProfileReady).thenReturn(true) `when`(leAudio.isProfileReady).thenReturn(true) `when`(leAudio.broadcastToUnicastFallbackGroup).thenReturn(TEST_GROUP_ID2) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2)) underTest.setSecondaryVolume(TEST_VOLUME1) runCurrent() verify(volumeControl).setDeviceVolume(device1, TEST_VOLUME1, true) Truth.assertThat(logger.logs) .isEqualTo( listOf( "onSetVolumeRequested volume=$TEST_VOLUME1", ) ) } } private fun triggerAudioSharingStateChange( type: TriggerType, broadcastAction: BluetoothLeBroadcast.Callback.() -> Unit Loading Loading @@ -404,6 +565,13 @@ class AudioSharingRepositoryTest { assistantCallbackCaptor.value.sourceAdded(device1) } private fun triggerSourceAddedWithGetPrimaryGroupApi() { verify(assistant).registerServiceCallBack(any(), assistantCallbackCaptor.capture()) `when`(leAudio.broadcastToUnicastFallbackGroup).thenReturn(TEST_GROUP_ID1) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2)) assistantCallbackCaptor.value.sourceAdded(device1) } private fun triggerSourceRemoved() { verify(assistant).registerServiceCallBack(any(), assistantCallbackCaptor.capture()) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1)) Loading @@ -415,6 +583,13 @@ class AudioSharingRepositoryTest { assistantCallbackCaptor.value.sourceRemoved(device2) } private fun triggerSourceRemovedWithGetPrimaryGroupApi() { verify(assistant).registerServiceCallBack(any(), assistantCallbackCaptor.capture()) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1)) `when`(leAudio.broadcastToUnicastFallbackGroup).thenReturn(TEST_GROUP_ID1) assistantCallbackCaptor.value.sourceRemoved(device2) } private fun triggerProfileConnectionChange(state: Int, profile: Int) { verify(eventManager).registerCallback(btCallbackCaptor.capture()) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1)) Loading @@ -426,6 +601,13 @@ class AudioSharingRepositoryTest { btCallbackCaptor.value.onProfileConnectionStateChanged(cachedDevice2, state, profile) } private fun triggerProfileConnectionChangeWithGetPrimaryGroupApi(state: Int, profile: Int) { verify(eventManager).registerCallback(btCallbackCaptor.capture()) `when`(assistant.allConnectedDevices).thenReturn(listOf(device1)) `when`(leAudio.broadcastToUnicastFallbackGroup).thenReturn(TEST_GROUP_ID1) btCallbackCaptor.value.onProfileConnectionStateChanged(cachedDevice2, state, profile) } private fun triggerContentObserverChange() { verify(contentResolver) .registerContentObserver( Loading @@ -442,6 +624,12 @@ class AudioSharingRepositoryTest { contentObserverCaptor.value.primaryChanged() } private fun triggerPrimaryGroupIdChanged() { verify(leAudio).registerCallback(any(), leAudioCallbackCaptor.capture()) `when`(leAudio.broadcastToUnicastFallbackGroup).thenReturn(TEST_GROUP_ID2) leAudioCallbackCaptor.value.onBroadcastToUnicastFallbackGroupChanged(TEST_GROUP_ID2) } private fun triggerVolumeMapChange(change: Pair<BluetoothDevice, Int>) { verify(volumeControl).registerCallback(any(), volumeCallbackCaptor.capture()) volumeCallbackCaptor.value.onDeviceVolumeChanged(change.first, change.second) Loading