[Audiosharing] Use new API to get primary group id change
Test: atest
Bug: 397568136
Flag: com.android.settingslib.flags.adopt_primary_group_management_api_v2
Change-Id: I06e8b51ba9714f48c035c92e78e697967815d0bb
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
index 8771ba0..98cae31 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
@@ -30,9 +30,11 @@
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
@@ -140,15 +142,26 @@
}
override val primaryGroupId: StateFlow<Int> =
- primaryChange
- .map { BluetoothUtils.getPrimaryGroupIdForBroadcast(contentResolver) }
- .onStart { emit(BluetoothUtils.getPrimaryGroupIdForBroadcast(contentResolver)) }
- .flowOn(backgroundCoroutineContext)
- .stateIn(
- coroutineScope,
- SharingStarted.WhileSubscribed(),
- BluetoothCsipSetCoordinator.GROUP_ID_INVALID
- )
+ 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(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(),
+ BluetoothCsipSetCoordinator.GROUP_ID_INVALID
+ )
override val primaryDevice: StateFlow<CachedBluetoothDevice?> =
primaryGroupId
@@ -273,8 +286,12 @@
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
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
index 0f25ae2..6fd5ab7 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
@@ -18,6 +18,7 @@
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothLeAudio
import android.bluetooth.BluetoothLeBroadcast
import android.bluetooth.BluetoothLeBroadcastAssistant
import android.bluetooth.BluetoothLeBroadcastReceiveState
@@ -26,6 +27,8 @@
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
@@ -36,11 +39,13 @@
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
@@ -83,6 +88,8 @@
@Mock private lateinit var volumeControl: VolumeControlProfile
+ @Mock private lateinit var leAudio: LeAudioProfile
+
@Mock private lateinit var eventManager: BluetoothEventManager
@Mock private lateinit var deviceManager: CachedBluetoothDeviceManager
@@ -104,6 +111,8 @@
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>
@@ -123,6 +132,7 @@
`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)
@@ -160,6 +170,7 @@
`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()
@@ -192,7 +203,8 @@
}
@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)
@@ -209,6 +221,29 @@
}
@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))
@@ -224,6 +259,26 @@
}
@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 {
val groupIds = mutableListOf<Int?>()
@@ -234,11 +289,13 @@
}
@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()
@@ -284,11 +341,65 @@
}
@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()
@@ -307,11 +418,37 @@
}
@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()
@@ -354,6 +491,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2)
fun setSecondaryVolume_setValue() {
testScope.runTest {
Settings.Secure.putInt(
@@ -375,6 +513,29 @@
}
}
+ @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
@@ -404,6 +565,13 @@
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))
@@ -415,6 +583,13 @@
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))
@@ -426,6 +601,13 @@
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(
@@ -442,6 +624,12 @@
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)