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

Commit 3019b303 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Avoid blocking media loads when converting from resume to active" into main

parents c59b0fe5 68b39435
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(