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 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(