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

Commit a1402e5f authored by Beth Thibodeau's avatar Beth Thibodeau
Browse files

Fix media state update issues

- Allow media data updates if `active` status changes
- Update `lastActive` when PlaybackState is updated directly via the session

Without these changes, some updates would be skipped by
MediaProcessingHelper#isSameMediaData, resulting in unexpected state and/or
ordering in the carousel.

Bug: 404532173
Fixes: 421445708
Test: manual - repro steps in bug
Test: atest LegacyMediaDataManagerImplTest MediaDataProcessorTest
Flag: EXEMPT bugfix
Change-Id: Ia5aa3dfa0f6cbdb0bab87c7a22c9ab949ceeefdb
parent e1c56a35
Loading
Loading
Loading
Loading
+13 −6
Original line number Diff line number Diff line
@@ -376,7 +376,7 @@ class LegacyMediaDataManagerImpl(
        isConvertingToActive: Boolean = false,
    ) =
        withContext(backgroundDispatcher) {
            val lastActive = systemClock.elapsedRealtime()
            val lastActive = getActiveTimestamp(systemClock)
            val result = mediaDataLoader.get().loadMediaData(key, sbn, isConvertingToActive)
            if (result == null) {
                Log.d(TAG, "No result from loadMediaData")
@@ -506,7 +506,7 @@ class LegacyMediaDataManagerImpl(
            }
            // Update last active if media was still active.
            if (it.active) {
                it.lastActive = systemClock.elapsedRealtime()
                it.lastActive = getActiveTimestamp(systemClock)
            }
            it.active = !timedOut
            if (DEBUG) Log.d(TAG, "Updating $key timedOut: $timedOut")
@@ -536,9 +536,16 @@ class LegacyMediaDataManagerImpl(
                // otherwise, no need to update semantic actions.
                val data =
                    if (actions != null) {
                        it.copy(semanticActions = actions, isPlaying = isPlayingState(state.state))
                        it.copy(
                            semanticActions = actions,
                            isPlaying = isPlayingState(state.state),
                            lastActive = getActiveTimestamp(systemClock),
                        )
                    } else {
                        it.copy(isPlaying = isPlayingState(state.state))
                        it.copy(
                            isPlaying = isPlayingState(state.state),
                            lastActive = getActiveTimestamp(systemClock),
                        )
                    }
                if (DEBUG) Log.d(TAG, "State updated outside of notification")
                foregroundExecutor.execute { onMediaDataLoaded(key, key, data) }
@@ -585,7 +592,7 @@ class LegacyMediaDataManagerImpl(
        packageName: String,
    ) =
        withContext(backgroundDispatcher) {
            val lastActive = systemClock.elapsedRealtime()
            val lastActive = getActiveTimestamp(systemClock)
            val currentEntry = mediaEntries[packageName]
            val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
            val result =
@@ -781,7 +788,7 @@ class LegacyMediaDataManagerImpl(
            }
        val lastActive =
            if (data.active) {
                systemClock.elapsedRealtime()
                getActiveTimestamp(systemClock)
            } else {
                data.lastActive
            }
+9 −5
Original line number Diff line number Diff line
@@ -363,7 +363,7 @@ class MediaDataProcessor(
            }
            // Update last active if media was still active.
            if (it.active) {
                it.lastActive = systemClock.elapsedRealtime()
                it.lastActive = getActiveTimestamp(systemClock)
            }
            it.active = !timedOut
            if (DEBUG) Log.d(TAG, "Updating $key timedOut: $timedOut")
@@ -397,9 +397,13 @@ class MediaDataProcessor(
                            it.copy(
                                semanticActions = actions,
                                isPlaying = isPlayingState(state.state),
                                lastActive = getActiveTimestamp(systemClock),
                            )
                        } else {
                            it.copy(isPlaying = isPlayingState(state.state))
                            it.copy(
                                isPlaying = isPlayingState(state.state),
                                lastActive = getActiveTimestamp(systemClock),
                            )
                        }
                    if (DEBUG) Log.d(TAG, "State updated outside of notification")
                    withContext(mainDispatcher) { onMediaDataLoaded(key, key, data) }
@@ -458,7 +462,7 @@ class MediaDataProcessor(
        packageName: String,
    ) =
        withContext(backgroundDispatcher) {
            val lastActive = systemClock.elapsedRealtime()
            val lastActive = getActiveTimestamp(systemClock)
            val currentEntry = mediaDataRepository.mediaEntries.value[packageName]
            val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
            val result =
@@ -524,7 +528,7 @@ class MediaDataProcessor(
        isConvertingToActive: Boolean = false,
    ) =
        withContext(backgroundDispatcher) {
            val lastActive = systemClock.elapsedRealtime()
            val lastActive = getActiveTimestamp(systemClock)
            val result = mediaDataLoader.get().loadMediaData(key, sbn, isConvertingToActive)
            if (result == null) {
                Log.d(TAG, "No result from loadMediaData")
@@ -744,7 +748,7 @@ class MediaDataProcessor(
            }
        val lastActive =
            if (data.active) {
                systemClock.elapsedRealtime()
                getActiveTimestamp(systemClock)
            } else {
                data.lastActive
            }
+4 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.os.BadParcelableException
import android.util.Log
import com.android.systemui.biometrics.Utils.toBitmap
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.util.time.SystemClock

private const val TAG = "MediaProcessingHelper"

@@ -62,6 +63,7 @@ fun isSameMediaData(
        new.resumption == old.resumption &&
        new.token == old.token &&
        new.resumeProgress == old.resumeProgress &&
        new.active == old.active &&
        areClickIntentsEqual(new.clickIntent, old.clickIntent) &&
        areActionsEqual(context, newController, new, old) &&
        areIconsEqual(context, new.artwork, old.artwork) &&
@@ -179,3 +181,5 @@ private fun areActionsEqual(
private fun areClickIntentsEqual(newIntent: PendingIntent?, oldIntent: PendingIntent?): Boolean {
    return newIntent == oldIntent
}

fun getActiveTimestamp(clock: SystemClock) = clock.elapsedRealtime()
+17 −1
Original line number Diff line number Diff line
@@ -1273,7 +1273,7 @@ class LegacyMediaDataManagerImplTest : SysuiTestCase() {
    }

    @Test
    fun testPlaybackState_PauseWhenFlagTrue_keyExists_callsListener() {
    fun testPlaybackState_Pause_keyExists_callsListener() {
        val state = PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 1f).build()
        whenever(controller.playbackState).thenReturn(state)

@@ -1345,6 +1345,22 @@ class LegacyMediaDataManagerImplTest : SysuiTestCase() {
        assertThat(mediaDataCaptor.value.semanticActions).isNull()
    }

    @Test
    fun testPlaybackStateChange_updatesLastActiveTime() {
        // Notification has been added
        val currentTime = clock.elapsedRealtime()
        addNotificationAndLoad()

        // Callback gets an updated state
        clock.advanceTime(1000)
        val state = PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0L, 1f).build()
        onStateUpdated(KEY, state)

        // Last active time is updated
        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true))
        assertThat(mediaDataCaptor.value.lastActive).isAtLeast(currentTime + 1000)
    }

    @Test
    fun testNoClearNotOngoing_canDismiss() {
        mediaNotification =
+14 −1
Original line number Diff line number Diff line
@@ -1326,7 +1326,7 @@ class MediaDataProcessorTest() : SysuiTestCase() {
    }

    @Test
    fun testPlaybackState_PauseWhenFlagTrue_keyExists_callsListener() {
    fun testPlaybackState_Pause_keyExists_callsListener() {
        val state = PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 1f).build()
        whenever(controller.playbackState).thenReturn(state)

@@ -1397,6 +1397,19 @@ class MediaDataProcessorTest() : SysuiTestCase() {
        assertThat(mediaDataCaptor.value.semanticActions).isNull()
    }

    @Test
    fun testPlaybackStateChange_updatesLastActiveTime() {
        val currentTime = clock.elapsedRealtime()
        addNotificationAndLoad()

        clock.advanceTime(1000)
        val state = PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0L, 1f).build()
        testScope.onStateUpdated(KEY, state)

        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true))
        assertThat(mediaDataCaptor.value.lastActive).isAtLeast(currentTime + 1000)
    }

    @Test
    fun testNoClearNotOngoing_canDismiss() {
        mediaNotification =