Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 290a6244 authored by Robert Snoeberger's avatar Robert Snoeberger
Browse files

Move call to MediaRouter2Manager to worker thread

The call to MediaRouter2Manager#getRoutingSessionForMediaController was
implicated in an ANR. So, moving the call to a background thread to
prevent it from being caught blocking the main thread.

Fixes: 161690149
Test: atest tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
Change-Id: Icf3945a971be84397a335672134b1067decfe6da
parent f57fbbbe
Loading
Loading
Loading
Loading
+26 −8
Original line number Diff line number Diff line
@@ -19,8 +19,12 @@ package com.android.systemui.media
import android.content.Context
import android.media.MediaRouter2Manager
import android.media.session.MediaController
import androidx.annotation.AnyThread
import androidx.annotation.MainThread
import androidx.annotation.WorkerThread
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
@@ -39,11 +43,12 @@ class MediaDeviceManager @Inject constructor(
    private val localMediaManagerFactory: LocalMediaManagerFactory,
    private val mr2manager: MediaRouter2Manager,
    @Main private val fgExecutor: Executor,
    @Background private val bgExecutor: Executor,
    private val mediaDataManager: MediaDataManager,
    private val dumpManager: DumpManager
) : MediaDataManager.Listener, Dumpable {
    private val listeners: MutableSet<Listener> = mutableSetOf()
    private val entries: MutableMap<String, Token> = mutableMapOf()
    private val entries: MutableMap<String, Entry> = mutableMapOf()

    init {
        mediaDataManager.addListener(this)
@@ -71,7 +76,7 @@ class MediaDeviceManager @Inject constructor(
            val controller = data.token?.let {
                MediaController(context, it)
            }
            entry = Token(key, oldKey, controller,
            entry = Entry(key, oldKey, controller,
                    localMediaManagerFactory.create(data.packageName))
            entries[key] = entry
            entry.start()
@@ -99,6 +104,7 @@ class MediaDeviceManager @Inject constructor(
        }
    }

    @MainThread
    private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) {
        val enabled = device != null
        val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name)
@@ -114,12 +120,13 @@ class MediaDeviceManager @Inject constructor(
        fun onKeyRemoved(key: String)
    }

    private inner class Token(
    private inner class Entry(
        val key: String,
        val oldKey: String?,
        val controller: MediaController?,
        val localMediaManager: LocalMediaManager
    ) : LocalMediaManager.DeviceCallback {

        val token
            get() = controller?.sessionToken
        private var started = false
@@ -127,20 +134,27 @@ class MediaDeviceManager @Inject constructor(
            set(value) {
                if (!started || value != field) {
                    field = value
                    fgExecutor.execute {
                        processDevice(key, oldKey, value)
                    }
                }
        fun start() {
            }

        @AnyThread
        fun start() = bgExecutor.execute {
            localMediaManager.registerCallback(this)
            localMediaManager.startScan()
            updateCurrent()
            started = true
        }
        fun stop() {

        @AnyThread
        fun stop() = bgExecutor.execute {
            started = false
            localMediaManager.stopScan()
            localMediaManager.unregisterCallback(this)
        }

        fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
            val route = controller?.let {
                mr2manager.getRoutingSessionForMediaController(it)
@@ -152,14 +166,18 @@ class MediaDeviceManager @Inject constructor(
                println("    route=$route")
            }
        }
        override fun onDeviceListUpdate(devices: List<MediaDevice>?) = fgExecutor.execute {

        override fun onDeviceListUpdate(devices: List<MediaDevice>?) = bgExecutor.execute {
            updateCurrent()
        }

        override fun onSelectedDeviceStateChanged(device: MediaDevice, state: Int) {
            fgExecutor.execute {
            bgExecutor.execute {
                updateCurrent()
            }
        }

        @WorkerThread
        private fun updateCurrent() {
            val device = localMediaManager.getCurrentConnectedDevice()
            controller?.let {
+36 −12
Original line number Diff line number Diff line
@@ -72,7 +72,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    @Mock private lateinit var lmmFactory: LocalMediaManagerFactory
    @Mock private lateinit var lmm: LocalMediaManager
    @Mock private lateinit var mr2: MediaRouter2Manager
    private lateinit var fakeExecutor: FakeExecutor
    private lateinit var fakeFgExecutor: FakeExecutor
    private lateinit var fakeBgExecutor: FakeExecutor
    @Mock private lateinit var dumpster: DumpManager
    @Mock private lateinit var listener: MediaDeviceManager.Listener
    @Mock private lateinit var device: MediaDevice
@@ -87,9 +88,10 @@ public class MediaDeviceManagerTest : SysuiTestCase() {

    @Before
    fun setUp() {
        fakeExecutor = FakeExecutor(FakeSystemClock())
        manager = MediaDeviceManager(context, lmmFactory, mr2, fakeExecutor, mediaDataManager,
                dumpster)
        fakeFgExecutor = FakeExecutor(FakeSystemClock())
        fakeBgExecutor = FakeExecutor(FakeSystemClock())
        manager = MediaDeviceManager(context, lmmFactory, mr2, fakeFgExecutor, fakeBgExecutor,
                mediaDataManager, dumpster)
        manager.addListener(listener)

        // Configure mocks.
@@ -144,13 +146,15 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    fun loadAndRemoveMediaData() {
        manager.onMediaDataLoaded(KEY, null, mediaData)
        manager.onMediaDataRemoved(KEY)
        fakeBgExecutor.runAllReady()
        verify(lmm).unregisterCallback(any())
    }

    @Test
    fun loadMediaDataWithNullToken() {
        manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
        fakeExecutor.runAllReady()
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        val data = captureDeviceData(KEY)
        assertThat(data.enabled).isTrue()
        assertThat(data.name).isEqualTo(DEVICE_NAME)
@@ -163,6 +167,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
        reset(listener)
        // WHEN data is loaded with a new key
        manager.onMediaDataLoaded(KEY, KEY_OLD, mediaData)
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        // THEN the listener for the old key should removed.
        verify(lmm).unregisterCallback(any())
        // AND a new device event emitted
@@ -186,6 +192,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    fun unknownOldKey() {
        val oldKey = "unknown"
        manager.onMediaDataLoaded(KEY, oldKey, mediaData)
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        verify(listener).onMediaDeviceChanged(eq(KEY), eq(oldKey), any())
    }

@@ -193,13 +201,16 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    fun updateToSessionTokenWithNullRoute() {
        // GIVEN that media data has been loaded with a null token
        manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        reset(listener)
        // WHEN media data is loaded with a different token
        // AND that token results in a null route
        reset(listener)
        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        // THEN the device should be disabled
        fakeExecutor.runAllReady()
        val data = captureDeviceData(KEY)
        assertThat(data.enabled).isFalse()
        assertThat(data.name).isNull()
@@ -210,7 +221,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    fun deviceEventOnAddNotification() {
        // WHEN a notification is added
        manager.onMediaDataLoaded(KEY, null, mediaData)
        val deviceCallback = captureCallback()
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        // THEN the update is dispatched to the listener
        val data = captureDeviceData(KEY)
        assertThat(data.enabled).isTrue()
@@ -230,10 +242,12 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    @Test
    fun deviceListUpdate() {
        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
        val deviceCallback = captureCallback()
        // WHEN the device list changes
        deviceCallback.onDeviceListUpdate(mutableListOf(device))
        assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
        assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1)
        assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
        // THEN the update is dispatched to the listener
        val data = captureDeviceData(KEY)
        assertThat(data.enabled).isTrue()
@@ -244,10 +258,12 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    @Test
    fun selectedDeviceStateChanged() {
        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
        val deviceCallback = captureCallback()
        // WHEN the selected device changes state
        deviceCallback.onSelectedDeviceStateChanged(device, 1)
        assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
        assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1)
        assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
        // THEN the update is dispatched to the listener
        val data = captureDeviceData(KEY)
        assertThat(data.enabled).isTrue()
@@ -270,6 +286,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
        // WHEN a notification is added
        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        // THEN the device is disabled
        val data = captureDeviceData(KEY)
        assertThat(data.enabled).isFalse()
@@ -281,13 +299,16 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceChanged() {
        // GIVEN a notif is added
        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        reset(listener)
        // AND MR2Manager returns null for routing session
        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
        // WHEN the selected device changes state
        val deviceCallback = captureCallback()
        deviceCallback.onSelectedDeviceStateChanged(device, 1)
        fakeExecutor.runAllReady()
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        // THEN the device is disabled
        val data = captureDeviceData(KEY)
        assertThat(data.enabled).isFalse()
@@ -299,13 +320,16 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceListUpdate() {
        // GIVEN a notif is added
        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        reset(listener)
        // GIVEN that MR2Manager returns null for routing session
        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
        // WHEN the selected device changes state
        val deviceCallback = captureCallback()
        deviceCallback.onDeviceListUpdate(mutableListOf(device))
        fakeExecutor.runAllReady()
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        // THEN the device is disabled
        val data = captureDeviceData(KEY)
        assertThat(data.enabled).isFalse()