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

Commit 68b39435 authored by Michael Mikhail's avatar Michael Mikhail
Browse files

Avoid blocking media loads when converting from resume to active

Flag: com.android.systemui.media_load_metadata_via_media_data_loader
Fixes: 368472246
Test: atest MediaDataLoaderTest
Test: Build, checked UI.
Change-Id: Ia0442ba88488e6214a4d9d49285d13bfad35d527
parent 2a8162ee
Loading
Loading
Loading
Loading
+34 −0
Original line number Original line Diff line number Diff line
@@ -408,6 +408,40 @@ class MediaDataLoaderTest : SysuiTestCase() {
            verify(mockImageLoader, times(1)).loadBitmap(any(), anyInt(), anyInt(), anyInt())
            verify(mockImageLoader, times(1)).loadBitmap(any(), anyInt(), anyInt(), anyInt())
        }
        }


    @OptIn(ExperimentalCoroutinesApi::class)
    @Test
    fun testLoadMediaDataInBg_fromResumeToActive_doesNotCancelResumeToActiveTask() =
        testScope.runTest {
            val mockImageLoader = mock<ImageLoader>()
            val mediaDataLoader =
                MediaDataLoader(
                    context,
                    testDispatcher,
                    testScope,
                    mediaControllerFactory,
                    mediaFlags,
                    mockImageLoader,
                    statusBarManager,
                )
            metadataBuilder.putString(
                MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
                "content://album_art_uri",
            )

            testScope.launch {
                mediaDataLoader.loadMediaData(
                    KEY,
                    createMediaNotification(),
                    isConvertingToActive = true,
                )
            }
            testScope.launch { mediaDataLoader.loadMediaData(KEY, createMediaNotification()) }
            testScope.launch { mediaDataLoader.loadMediaData(KEY, createMediaNotification()) }
            testScope.advanceUntilIdle()

            verify(mockImageLoader, times(2)).loadBitmap(any(), anyInt(), anyInt(), anyInt())
        }

    private fun createMediaNotification(
    private fun createMediaNotification(
        mediaSession: MediaSession? = session,
        mediaSession: MediaSession? = session,
        applicationInfo: ApplicationInfo? = null,
        applicationInfo: ApplicationInfo? = null,
+7 −3
Original line number Original line Diff line number Diff line
@@ -417,6 +417,7 @@ class LegacyMediaDataManagerImpl(
    override fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
    override fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
        if (useQsMediaPlayer && isMediaNotification(sbn)) {
        if (useQsMediaPlayer && isMediaNotification(sbn)) {
            var isNewlyActiveEntry = false
            var isNewlyActiveEntry = false
            var isConvertingToActive = false
            Assert.isMainThread()
            Assert.isMainThread()
            val oldKey = findExistingEntry(key, sbn.packageName)
            val oldKey = findExistingEntry(key, sbn.packageName)
            if (oldKey == null) {
            if (oldKey == null) {
@@ -433,9 +434,10 @@ class LegacyMediaDataManagerImpl(
                // Resume -> active conversion; move to new key
                // Resume -> active conversion; move to new key
                val oldData = mediaEntries.remove(oldKey)!!
                val oldData = mediaEntries.remove(oldKey)!!
                isNewlyActiveEntry = true
                isNewlyActiveEntry = true
                isConvertingToActive = true
                mediaEntries.put(key, oldData)
                mediaEntries.put(key, oldData)
            }
            }
            loadMediaData(key, sbn, oldKey, isNewlyActiveEntry)
            loadMediaData(key, sbn, oldKey, isNewlyActiveEntry, isConvertingToActive)
        } else {
        } else {
            onNotificationRemoved(key)
            onNotificationRemoved(key)
        }
        }
@@ -535,10 +537,11 @@ class LegacyMediaDataManagerImpl(
        sbn: StatusBarNotification,
        sbn: StatusBarNotification,
        oldKey: String?,
        oldKey: String?,
        isNewlyActiveEntry: Boolean = false,
        isNewlyActiveEntry: Boolean = false,
        isConvertingToActive: Boolean = false,
    ) {
    ) {
        if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
        if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
            applicationScope.launch {
            applicationScope.launch {
                loadMediaDataWithLoader(key, sbn, oldKey, isNewlyActiveEntry)
                loadMediaDataWithLoader(key, sbn, oldKey, isNewlyActiveEntry, isConvertingToActive)
            }
            }
        } else {
        } else {
            backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) }
            backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) }
@@ -550,10 +553,11 @@ class LegacyMediaDataManagerImpl(
        sbn: StatusBarNotification,
        sbn: StatusBarNotification,
        oldKey: String?,
        oldKey: String?,
        isNewlyActiveEntry: Boolean = false,
        isNewlyActiveEntry: Boolean = false,
        isConvertingToActive: Boolean = false,
    ) =
    ) =
        withContext(backgroundDispatcher) {
        withContext(backgroundDispatcher) {
            val lastActive = systemClock.elapsedRealtime()
            val lastActive = systemClock.elapsedRealtime()
            val result = mediaDataLoader.get().loadMediaData(key, sbn)
            val result = mediaDataLoader.get().loadMediaData(key, sbn, isConvertingToActive)
            if (result == null) {
            if (result == null) {
                Log.d(TAG, "No result from loadMediaData")
                Log.d(TAG, "No result from loadMediaData")
                return@withContext
                return@withContext
+19 −5
Original line number Original line Diff line number Diff line
@@ -111,16 +111,26 @@ constructor(
     * If a new [loadMediaData] is issued while existing load is in progress, the existing (old)
     * If a new [loadMediaData] is issued while existing load is in progress, the existing (old)
     * load will be cancelled.
     * load will be cancelled.
     */
     */
    suspend fun loadMediaData(key: String, sbn: StatusBarNotification): MediaDataLoaderResult? {
    suspend fun loadMediaData(
        val loadMediaJob = backgroundScope.async { loadMediaDataInBackground(key, sbn) }
        key: String,
        sbn: StatusBarNotification,
        isConvertingToActive: Boolean = false,
    ): MediaDataLoaderResult? {
        val loadMediaJob =
            backgroundScope.async { loadMediaDataInBackground(key, sbn, isConvertingToActive) }
        loadMediaJob.invokeOnCompletion {
        loadMediaJob.invokeOnCompletion {
            // We need to make sure we're removing THIS job after cancellation, not
            // We need to make sure we're removing THIS job after cancellation, not
            // a job that we created later.
            // a job that we created later.
            mediaProcessingJobs.remove(key, loadMediaJob)
            mediaProcessingJobs.remove(key, loadMediaJob)
        }
        }
        val existingJob = mediaProcessingJobs.put(key, loadMediaJob)
        var existingJob: Job? = null
        logD(TAG) { "Loading media data for $key... / existing job: $existingJob" }
        // Do not cancel loading jobs that convert resume players to active.
        if (!isConvertingToActive) {
            existingJob = mediaProcessingJobs.put(key, loadMediaJob)
            existingJob?.cancel("New processing job incoming.")
            existingJob?.cancel("New processing job incoming.")
        }
        logD(TAG) { "Loading media data for $key... / existing job: $existingJob" }

        return loadMediaJob.await()
        return loadMediaJob.await()
    }
    }


@@ -129,12 +139,16 @@ constructor(
    private suspend fun loadMediaDataInBackground(
    private suspend fun loadMediaDataInBackground(
        key: String,
        key: String,
        sbn: StatusBarNotification,
        sbn: StatusBarNotification,
        isConvertingToActive: Boolean = false,
    ): MediaDataLoaderResult? =
    ): MediaDataLoaderResult? =
        traceCoroutine("MediaDataLoader#loadMediaData") {
        traceCoroutine("MediaDataLoader#loadMediaData") {
            // We have apps spamming us with quick notification updates which can cause
            // We have apps spamming us with quick notification updates which can cause
            // us to spend significant CPU time loading duplicate data. This debounces
            // us to spend significant CPU time loading duplicate data. This debounces
            // those requests at the cost of a bit of latency.
            // those requests at the cost of a bit of latency.
            // No delay needed to load jobs converting resume players to active.
            if (!isConvertingToActive) {
                delay(DEBOUNCE_DELAY_MS)
                delay(DEBOUNCE_DELAY_MS)
            }


            val token =
            val token =
                sbn.notification.extras.getParcelable(
                sbn.notification.extras.getParcelable(