Loading packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -1400,6 +1400,16 @@ flag { } } flag { name: "media_controls_device_manager_background_execution" namespace: "systemui" description: "Sends some instances creation to background thread" bug: "400200474" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "output_switcher_redesign" namespace: "systemui" Loading packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt +59 −32 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice import com.android.settingslib.media.PhoneMediaDevice import com.android.settingslib.media.flags.Flags import com.android.systemui.Flags.mediaControlsDeviceManagerBackgroundExecution import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.controls.shared.MediaControlDrawables Loading Loading @@ -94,8 +95,39 @@ constructor( data: MediaData, immediately: Boolean, receivedSmartspaceCardLatency: Int, isSsReactivated: Boolean isSsReactivated: Boolean, ) { if (mediaControlsDeviceManagerBackgroundExecution()) { bgExecutor.execute { onMediaLoaded(key, oldKey, data) } } else { onMediaLoaded(key, oldKey, data) } } override fun onMediaDataRemoved(key: String, userInitiated: Boolean) { if (mediaControlsDeviceManagerBackgroundExecution()) { bgExecutor.execute { onMediaRemoved(key, userInitiated) } } else { onMediaRemoved(key, userInitiated) } } fun dump(pw: PrintWriter) { with(pw) { println("MediaDeviceManager state:") entries.forEach { (key, entry) -> println(" key=$key") entry.dump(pw) } } } @MainThread private fun processDevice(key: String, oldKey: String?, device: MediaDeviceData?) { listeners.forEach { it.onMediaDeviceChanged(key, oldKey, device) } } private fun onMediaLoaded(key: String, oldKey: String?, data: MediaData) { if (oldKey != null && oldKey != key) { val oldEntry = entries.remove(oldKey) oldEntry?.stop() Loading @@ -104,9 +136,13 @@ constructor( if (entry == null || entry.token != data.token) { entry?.stop() if (data.device != null) { // If we were already provided device info (e.g. from RCN), keep that and don't // listen for updates, but process once to push updates to listeners // If we were already provided device info (e.g. from RCN), keep that and // don't listen for updates, but process once to push updates to listeners if (mediaControlsDeviceManagerBackgroundExecution()) { fgExecutor.execute { processDevice(key, oldKey, data.device) } } else { processDevice(key, oldKey, data.device) } return } val controller = data.token?.let { controllerFactory.create(it) } Loading @@ -120,25 +156,16 @@ constructor( } } override fun onMediaDataRemoved(key: String, userInitiated: Boolean) { private fun onMediaRemoved(key: String, userInitiated: Boolean) { val token = entries.remove(key) token?.stop() if (mediaControlsDeviceManagerBackgroundExecution()) { fgExecutor.execute { token?.let { listeners.forEach { it.onKeyRemoved(key, userInitiated) } } } fun dump(pw: PrintWriter) { with(pw) { println("MediaDeviceManager state:") entries.forEach { (key, entry) -> println(" key=$key") entry.dump(pw) } } } else { token?.let { listeners.forEach { it.onKeyRemoved(key, userInitiated) } } } @MainThread private fun processDevice(key: String, oldKey: String?, device: MediaDeviceData?) { listeners.forEach { it.onMediaDeviceChanged(key, oldKey, device) } } interface Listener { Loading Loading @@ -260,7 +287,7 @@ constructor( override fun onAboutToConnectDeviceAdded( deviceAddress: String, deviceName: String, deviceIcon: Drawable? deviceIcon: Drawable?, ) { aboutToConnectDeviceOverride = AboutToConnectDevice( Loading @@ -270,8 +297,8 @@ constructor( /* enabled */ enabled = true, /* icon */ deviceIcon, /* name */ deviceName, /* showBroadcastButton */ showBroadcastButton = false ) /* showBroadcastButton */ showBroadcastButton = false, ), ) updateCurrent() } Loading @@ -292,7 +319,7 @@ constructor( override fun onBroadcastMetadataChanged( broadcastId: Int, metadata: BluetoothLeBroadcastMetadata metadata: BluetoothLeBroadcastMetadata, ) { logger.logBroadcastMetadataChanged(broadcastId, metadata.toString()) updateCurrent() Loading Loading @@ -352,14 +379,14 @@ constructor( // route. connectedDevice?.copy( name = it.name ?: connectedDevice.name, icon = icon icon = icon, ) } ?: MediaDeviceData( enabled = false, icon = MediaControlDrawables.getHomeDevices(context), name = context.getString(R.string.media_seamless_other_device), showBroadcastButton = false showBroadcastButton = false, ) logger.logRemoteDevice(routingSession?.name, connectedDevice) } else { Loading Loading @@ -398,7 +425,7 @@ constructor( device?.iconWithoutBackground, name, id = device?.id, showBroadcastButton = false showBroadcastButton = false, ) } } Loading @@ -415,7 +442,7 @@ constructor( icon = iconWithoutBackground, name = name, id = id, showBroadcastButton = false showBroadcastButton = false, ) private fun getLeAudioBroadcastDeviceData(): MediaDeviceData { Loading @@ -425,7 +452,7 @@ constructor( icon = MediaControlDrawables.getLeAudioSharing(context), name = context.getString(R.string.audio_sharing_description), intent = null, showBroadcastButton = false showBroadcastButton = false, ) } else { MediaDeviceData( Loading @@ -433,7 +460,7 @@ constructor( icon = MediaControlDrawables.getAntenna(context), name = broadcastDescription, intent = null, showBroadcastButton = true showBroadcastButton = true, ) } } Loading @@ -449,7 +476,7 @@ constructor( device, controller, routingSession?.name, selectedRoutes?.firstOrNull()?.name selectedRoutes?.firstOrNull()?.name, ) if (controller == null) { Loading Loading @@ -514,7 +541,7 @@ constructor( MediaDataUtils.getAppLabel( context, localMediaManager.packageName, context.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name) context.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name), ) val isCurrentBroadcastedApp = TextUtils.equals(mediaApp, currentBroadcastedApp) if (isCurrentBroadcastedApp) { Loading @@ -538,5 +565,5 @@ constructor( */ private data class AboutToConnectDevice( val fullMediaDevice: MediaDevice? = null, val backupMediaDeviceData: MediaDeviceData? = null val backupMediaDeviceData: MediaDeviceData? = null, ) packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt +23 −4 Original line number Diff line number Diff line Loading @@ -33,8 +33,8 @@ import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.RequiresFlagsDisabled import android.platform.test.annotations.RequiresFlagsEnabled import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.platform.test.flag.junit.FlagsParameterization import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast import com.android.settingslib.bluetooth.LocalBluetoothManager Loading Loading @@ -77,6 +77,8 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit import org.mockito.kotlin.eq import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters private const val KEY = "TEST_KEY" private const val KEY_OLD = "TEST_KEY_OLD" Loading @@ -89,12 +91,24 @@ private const val BROADCAST_APP_NAME = "BROADCAST_APP_NAME" private const val NORMAL_APP_NAME = "NORMAL_APP_NAME" @SmallTest @RunWith(AndroidJUnit4::class) @RunWith(ParameterizedAndroidJunit4::class) @TestableLooper.RunWithLooper public class MediaDeviceManagerTest : SysuiTestCase() { public class MediaDeviceManagerTest(flags: FlagsParameterization) : SysuiTestCase() { private companion object { companion object { val OTHER_DEVICE_ICON_STUB = TestStubDrawable() @JvmStatic @Parameters(name = "{0}") fun getParams(): List<FlagsParameterization> { return FlagsParameterization.progressionOf( com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DEVICE_MANAGER_BACKGROUND_EXECUTION ) } } init { mSetFlagsRule.setFlagsParameterization(flags) } @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() Loading Loading @@ -187,6 +201,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { @Test fun loadMediaData() { manager.onMediaDataLoaded(KEY, null, mediaData) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() verify(lmmFactory).create(PACKAGE) } Loading @@ -195,6 +211,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { manager.onMediaDataLoaded(KEY, null, mediaData) manager.onMediaDataRemoved(KEY, false) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() verify(lmm).unregisterCallback(any()) verify(muteAwaitManager).stopListening() } Loading Loading @@ -406,6 +423,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { manager.onMediaDataLoaded(KEY, null, mediaData) // WHEN the notification is removed manager.onMediaDataRemoved(KEY, true) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() // THEN the listener receives key removed event verify(listener).onKeyRemoved(eq(KEY), eq(true)) } Loading Loading
packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -1400,6 +1400,16 @@ flag { } } flag { name: "media_controls_device_manager_background_execution" namespace: "systemui" description: "Sends some instances creation to background thread" bug: "400200474" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "output_switcher_redesign" namespace: "systemui" Loading
packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt +59 −32 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice import com.android.settingslib.media.PhoneMediaDevice import com.android.settingslib.media.flags.Flags import com.android.systemui.Flags.mediaControlsDeviceManagerBackgroundExecution import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.controls.shared.MediaControlDrawables Loading Loading @@ -94,8 +95,39 @@ constructor( data: MediaData, immediately: Boolean, receivedSmartspaceCardLatency: Int, isSsReactivated: Boolean isSsReactivated: Boolean, ) { if (mediaControlsDeviceManagerBackgroundExecution()) { bgExecutor.execute { onMediaLoaded(key, oldKey, data) } } else { onMediaLoaded(key, oldKey, data) } } override fun onMediaDataRemoved(key: String, userInitiated: Boolean) { if (mediaControlsDeviceManagerBackgroundExecution()) { bgExecutor.execute { onMediaRemoved(key, userInitiated) } } else { onMediaRemoved(key, userInitiated) } } fun dump(pw: PrintWriter) { with(pw) { println("MediaDeviceManager state:") entries.forEach { (key, entry) -> println(" key=$key") entry.dump(pw) } } } @MainThread private fun processDevice(key: String, oldKey: String?, device: MediaDeviceData?) { listeners.forEach { it.onMediaDeviceChanged(key, oldKey, device) } } private fun onMediaLoaded(key: String, oldKey: String?, data: MediaData) { if (oldKey != null && oldKey != key) { val oldEntry = entries.remove(oldKey) oldEntry?.stop() Loading @@ -104,9 +136,13 @@ constructor( if (entry == null || entry.token != data.token) { entry?.stop() if (data.device != null) { // If we were already provided device info (e.g. from RCN), keep that and don't // listen for updates, but process once to push updates to listeners // If we were already provided device info (e.g. from RCN), keep that and // don't listen for updates, but process once to push updates to listeners if (mediaControlsDeviceManagerBackgroundExecution()) { fgExecutor.execute { processDevice(key, oldKey, data.device) } } else { processDevice(key, oldKey, data.device) } return } val controller = data.token?.let { controllerFactory.create(it) } Loading @@ -120,25 +156,16 @@ constructor( } } override fun onMediaDataRemoved(key: String, userInitiated: Boolean) { private fun onMediaRemoved(key: String, userInitiated: Boolean) { val token = entries.remove(key) token?.stop() if (mediaControlsDeviceManagerBackgroundExecution()) { fgExecutor.execute { token?.let { listeners.forEach { it.onKeyRemoved(key, userInitiated) } } } fun dump(pw: PrintWriter) { with(pw) { println("MediaDeviceManager state:") entries.forEach { (key, entry) -> println(" key=$key") entry.dump(pw) } } } else { token?.let { listeners.forEach { it.onKeyRemoved(key, userInitiated) } } } @MainThread private fun processDevice(key: String, oldKey: String?, device: MediaDeviceData?) { listeners.forEach { it.onMediaDeviceChanged(key, oldKey, device) } } interface Listener { Loading Loading @@ -260,7 +287,7 @@ constructor( override fun onAboutToConnectDeviceAdded( deviceAddress: String, deviceName: String, deviceIcon: Drawable? deviceIcon: Drawable?, ) { aboutToConnectDeviceOverride = AboutToConnectDevice( Loading @@ -270,8 +297,8 @@ constructor( /* enabled */ enabled = true, /* icon */ deviceIcon, /* name */ deviceName, /* showBroadcastButton */ showBroadcastButton = false ) /* showBroadcastButton */ showBroadcastButton = false, ), ) updateCurrent() } Loading @@ -292,7 +319,7 @@ constructor( override fun onBroadcastMetadataChanged( broadcastId: Int, metadata: BluetoothLeBroadcastMetadata metadata: BluetoothLeBroadcastMetadata, ) { logger.logBroadcastMetadataChanged(broadcastId, metadata.toString()) updateCurrent() Loading Loading @@ -352,14 +379,14 @@ constructor( // route. connectedDevice?.copy( name = it.name ?: connectedDevice.name, icon = icon icon = icon, ) } ?: MediaDeviceData( enabled = false, icon = MediaControlDrawables.getHomeDevices(context), name = context.getString(R.string.media_seamless_other_device), showBroadcastButton = false showBroadcastButton = false, ) logger.logRemoteDevice(routingSession?.name, connectedDevice) } else { Loading Loading @@ -398,7 +425,7 @@ constructor( device?.iconWithoutBackground, name, id = device?.id, showBroadcastButton = false showBroadcastButton = false, ) } } Loading @@ -415,7 +442,7 @@ constructor( icon = iconWithoutBackground, name = name, id = id, showBroadcastButton = false showBroadcastButton = false, ) private fun getLeAudioBroadcastDeviceData(): MediaDeviceData { Loading @@ -425,7 +452,7 @@ constructor( icon = MediaControlDrawables.getLeAudioSharing(context), name = context.getString(R.string.audio_sharing_description), intent = null, showBroadcastButton = false showBroadcastButton = false, ) } else { MediaDeviceData( Loading @@ -433,7 +460,7 @@ constructor( icon = MediaControlDrawables.getAntenna(context), name = broadcastDescription, intent = null, showBroadcastButton = true showBroadcastButton = true, ) } } Loading @@ -449,7 +476,7 @@ constructor( device, controller, routingSession?.name, selectedRoutes?.firstOrNull()?.name selectedRoutes?.firstOrNull()?.name, ) if (controller == null) { Loading Loading @@ -514,7 +541,7 @@ constructor( MediaDataUtils.getAppLabel( context, localMediaManager.packageName, context.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name) context.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name), ) val isCurrentBroadcastedApp = TextUtils.equals(mediaApp, currentBroadcastedApp) if (isCurrentBroadcastedApp) { Loading @@ -538,5 +565,5 @@ constructor( */ private data class AboutToConnectDevice( val fullMediaDevice: MediaDevice? = null, val backupMediaDeviceData: MediaDeviceData? = null val backupMediaDeviceData: MediaDeviceData? = null, )
packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt +23 −4 Original line number Diff line number Diff line Loading @@ -33,8 +33,8 @@ import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.RequiresFlagsDisabled import android.platform.test.annotations.RequiresFlagsEnabled import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.platform.test.flag.junit.FlagsParameterization import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast import com.android.settingslib.bluetooth.LocalBluetoothManager Loading Loading @@ -77,6 +77,8 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit import org.mockito.kotlin.eq import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters private const val KEY = "TEST_KEY" private const val KEY_OLD = "TEST_KEY_OLD" Loading @@ -89,12 +91,24 @@ private const val BROADCAST_APP_NAME = "BROADCAST_APP_NAME" private const val NORMAL_APP_NAME = "NORMAL_APP_NAME" @SmallTest @RunWith(AndroidJUnit4::class) @RunWith(ParameterizedAndroidJunit4::class) @TestableLooper.RunWithLooper public class MediaDeviceManagerTest : SysuiTestCase() { public class MediaDeviceManagerTest(flags: FlagsParameterization) : SysuiTestCase() { private companion object { companion object { val OTHER_DEVICE_ICON_STUB = TestStubDrawable() @JvmStatic @Parameters(name = "{0}") fun getParams(): List<FlagsParameterization> { return FlagsParameterization.progressionOf( com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DEVICE_MANAGER_BACKGROUND_EXECUTION ) } } init { mSetFlagsRule.setFlagsParameterization(flags) } @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() Loading Loading @@ -187,6 +201,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { @Test fun loadMediaData() { manager.onMediaDataLoaded(KEY, null, mediaData) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() verify(lmmFactory).create(PACKAGE) } Loading @@ -195,6 +211,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { manager.onMediaDataLoaded(KEY, null, mediaData) manager.onMediaDataRemoved(KEY, false) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() verify(lmm).unregisterCallback(any()) verify(muteAwaitManager).stopListening() } Loading Loading @@ -406,6 +423,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { manager.onMediaDataLoaded(KEY, null, mediaData) // WHEN the notification is removed manager.onMediaDataRemoved(KEY, true) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() // THEN the listener receives key removed event verify(listener).onKeyRemoved(eq(KEY), eq(true)) } Loading