Loading packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt +12 −7 Original line number Diff line number Diff line Loading @@ -1343,13 +1343,9 @@ class MediaDataManager( fun onNotificationRemoved(key: String) { Assert.isMainThread() val removed = mediaEntries.remove(key) ?: return val isEligibleForResume = removed.isLocalSession() || (mediaFlags.isRemoteResumeAllowed() && removed.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE) if (keyguardUpdateMonitor.isUserInLockdown(removed.userId)) { logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) } else if (useMediaResumption && removed.resumeAction != null && isEligibleForResume) { } else if (isAbleToResume(removed)) { convertToResumePlayer(key, removed) } else if (mediaFlags.isRetainingPlayersEnabled()) { handlePossibleRemoval(key, removed, notificationRemoved = true) Loading @@ -1369,6 +1365,14 @@ class MediaDataManager( handlePossibleRemoval(key, updated) } private fun isAbleToResume(data: MediaData): Boolean { val isEligibleForResume = data.isLocalSession() || (mediaFlags.isRemoteResumeAllowed() && data.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE) return useMediaResumption && data.resumeAction != null && isEligibleForResume } /** * Convert to resume state if the player is no longer valid and active, then notify listeners * that the data was updated. Does not convert to resume state if the player is still valid, or Loading @@ -1391,8 +1395,9 @@ class MediaDataManager( if (DEBUG) Log.d(TAG, "Session destroyed but using notification actions $key") mediaEntries.put(key, removed) notifyMediaDataLoaded(key, key, removed) } else if (removed.active) { // This player was still active - it didn't last long enough to time out: remove } else if (removed.active && !isAbleToResume(removed)) { // This player was still active - it didn't last long enough to time out, // and its app doesn't normally support resume: remove if (DEBUG) Log.d(TAG, "Removing still-active player $key") notifyMediaDataRemoved(key) logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) Loading packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt +35 −1 Original line number Diff line number Diff line Loading @@ -2031,7 +2031,7 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test fun testRetain_sessionPlayer_destroyedWhileActive_fullyRemoved() { fun testRetain_sessionPlayer_destroyedWhileActive_noResume_fullyRemoved() { whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) addPlaybackStateAction() Loading @@ -2050,6 +2050,40 @@ class MediaDataManagerTest : SysuiTestCase() { .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } @Test fun testRetain_sessionPlayer_canResume_destroyedWhileActive_setToResume() { whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) addPlaybackStateAction() // When a media control using session actions and that does allow resumption is added, addNotificationAndLoad() val dataResumable = mediaDataCaptor.value.copy(resumeAction = Runnable {}) mediaDataManager.onMediaDataLoaded(KEY, null, dataResumable) // And then the session is destroyed without timing out first sessionCallbackCaptor.value.invoke(KEY) // It is converted to a resume player verify(listener) .onMediaDataLoaded( eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), eq(0), eq(false) ) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.active).isFalse() verify(logger) .logActiveConvertedToResume( anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId) ) } @Test fun testSessionDestroyed_noNotificationKey_stillRemoved() { whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) Loading Loading
packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt +12 −7 Original line number Diff line number Diff line Loading @@ -1343,13 +1343,9 @@ class MediaDataManager( fun onNotificationRemoved(key: String) { Assert.isMainThread() val removed = mediaEntries.remove(key) ?: return val isEligibleForResume = removed.isLocalSession() || (mediaFlags.isRemoteResumeAllowed() && removed.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE) if (keyguardUpdateMonitor.isUserInLockdown(removed.userId)) { logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) } else if (useMediaResumption && removed.resumeAction != null && isEligibleForResume) { } else if (isAbleToResume(removed)) { convertToResumePlayer(key, removed) } else if (mediaFlags.isRetainingPlayersEnabled()) { handlePossibleRemoval(key, removed, notificationRemoved = true) Loading @@ -1369,6 +1365,14 @@ class MediaDataManager( handlePossibleRemoval(key, updated) } private fun isAbleToResume(data: MediaData): Boolean { val isEligibleForResume = data.isLocalSession() || (mediaFlags.isRemoteResumeAllowed() && data.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE) return useMediaResumption && data.resumeAction != null && isEligibleForResume } /** * Convert to resume state if the player is no longer valid and active, then notify listeners * that the data was updated. Does not convert to resume state if the player is still valid, or Loading @@ -1391,8 +1395,9 @@ class MediaDataManager( if (DEBUG) Log.d(TAG, "Session destroyed but using notification actions $key") mediaEntries.put(key, removed) notifyMediaDataLoaded(key, key, removed) } else if (removed.active) { // This player was still active - it didn't last long enough to time out: remove } else if (removed.active && !isAbleToResume(removed)) { // This player was still active - it didn't last long enough to time out, // and its app doesn't normally support resume: remove if (DEBUG) Log.d(TAG, "Removing still-active player $key") notifyMediaDataRemoved(key) logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) Loading
packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt +35 −1 Original line number Diff line number Diff line Loading @@ -2031,7 +2031,7 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test fun testRetain_sessionPlayer_destroyedWhileActive_fullyRemoved() { fun testRetain_sessionPlayer_destroyedWhileActive_noResume_fullyRemoved() { whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) addPlaybackStateAction() Loading @@ -2050,6 +2050,40 @@ class MediaDataManagerTest : SysuiTestCase() { .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } @Test fun testRetain_sessionPlayer_canResume_destroyedWhileActive_setToResume() { whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) addPlaybackStateAction() // When a media control using session actions and that does allow resumption is added, addNotificationAndLoad() val dataResumable = mediaDataCaptor.value.copy(resumeAction = Runnable {}) mediaDataManager.onMediaDataLoaded(KEY, null, dataResumable) // And then the session is destroyed without timing out first sessionCallbackCaptor.value.invoke(KEY) // It is converted to a resume player verify(listener) .onMediaDataLoaded( eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), eq(0), eq(false) ) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.active).isFalse() verify(logger) .logActiveConvertedToResume( anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId) ) } @Test fun testSessionDestroyed_noNotificationKey_stillRemoved() { whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) Loading