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

Commit c92535f0 authored by Michael Mikhail's avatar Michael Mikhail Committed by Android Build Coastguard Worker
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.
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:68b39435df3d8af5ff661e298a58a6f78f070610)
Merged-In: Ia0442ba88488e6214a4d9d49285d13bfad35d527
Change-Id: Ia0442ba88488e6214a4d9d49285d13bfad35d527
parent aa8a386c
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -408,6 +408,40 @@ class MediaDataLoaderTest : SysuiTestCase() {
            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(
        mediaSession: MediaSession? = session,
        applicationInfo: ApplicationInfo? = null,
+7 −3
Original line number Diff line number Diff line
@@ -417,6 +417,7 @@ class LegacyMediaDataManagerImpl(
    override fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
        if (useQsMediaPlayer && isMediaNotification(sbn)) {
            var isNewlyActiveEntry = false
            var isConvertingToActive = false
            Assert.isMainThread()
            val oldKey = findExistingEntry(key, sbn.packageName)
            if (oldKey == null) {
@@ -433,9 +434,10 @@ class LegacyMediaDataManagerImpl(
                // Resume -> active conversion; move to new key
                val oldData = mediaEntries.remove(oldKey)!!
                isNewlyActiveEntry = true
                isConvertingToActive = true
                mediaEntries.put(key, oldData)
            }
            loadMediaData(key, sbn, oldKey, isNewlyActiveEntry)
            loadMediaData(key, sbn, oldKey, isNewlyActiveEntry, isConvertingToActive)
        } else {
            onNotificationRemoved(key)
        }
@@ -535,10 +537,11 @@ class LegacyMediaDataManagerImpl(
        sbn: StatusBarNotification,
        oldKey: String?,
        isNewlyActiveEntry: Boolean = false,
        isConvertingToActive: Boolean = false,
    ) {
        if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
            applicationScope.launch {
                loadMediaDataWithLoader(key, sbn, oldKey, isNewlyActiveEntry)
                loadMediaDataWithLoader(key, sbn, oldKey, isNewlyActiveEntry, isConvertingToActive)
            }
        } else {
            backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) }
@@ -550,10 +553,11 @@ class LegacyMediaDataManagerImpl(
        sbn: StatusBarNotification,
        oldKey: String?,
        isNewlyActiveEntry: Boolean = false,
        isConvertingToActive: Boolean = false,
    ) =
        withContext(backgroundDispatcher) {
            val lastActive = systemClock.elapsedRealtime()
            val result = mediaDataLoader.get().loadMediaData(key, sbn)
            val result = mediaDataLoader.get().loadMediaData(key, sbn, isConvertingToActive)
            if (result == null) {
                Log.d(TAG, "No result from loadMediaData")
                return@withContext
+19 −5
Original line number 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)
     * load will be cancelled.
     */
    suspend fun loadMediaData(key: String, sbn: StatusBarNotification): MediaDataLoaderResult? {
        val loadMediaJob = backgroundScope.async { loadMediaDataInBackground(key, sbn) }
    suspend fun loadMediaData(
        key: String,
        sbn: StatusBarNotification,
        isConvertingToActive: Boolean = false,
    ): MediaDataLoaderResult? {
        val loadMediaJob =
            backgroundScope.async { loadMediaDataInBackground(key, sbn, isConvertingToActive) }
        loadMediaJob.invokeOnCompletion {
            // We need to make sure we're removing THIS job after cancellation, not
            // a job that we created later.
            mediaProcessingJobs.remove(key, loadMediaJob)
        }
        val existingJob = mediaProcessingJobs.put(key, loadMediaJob)
        logD(TAG) { "Loading media data for $key... / existing job: $existingJob" }
        var existingJob: Job? = null
        // Do not cancel loading jobs that convert resume players to active.
        if (!isConvertingToActive) {
            existingJob = mediaProcessingJobs.put(key, loadMediaJob)
            existingJob?.cancel("New processing job incoming.")
        }
        logD(TAG) { "Loading media data for $key... / existing job: $existingJob" }

        return loadMediaJob.await()
    }

@@ -129,12 +139,16 @@ constructor(
    private suspend fun loadMediaDataInBackground(
        key: String,
        sbn: StatusBarNotification,
        isConvertingToActive: Boolean = false,
    ): MediaDataLoaderResult? =
        traceCoroutine("MediaDataLoader#loadMediaData") {
            // We have apps spamming us with quick notification updates which can cause
            // us to spend significant CPU time loading duplicate data. This debounces
            // 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)
            }

            val token =
                sbn.notification.extras.getParcelable(