Limit number of resume controls
- When a new resume control is added past the max amount, remove the
oldest resume control
- Reduce resume control timeout to 48 hrs
Test: atest MediaDataManager
Bug: 267604946
Change-Id: I09c16475995521c1419ad3c645033c1fb5d70390
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 0a94803..db77777 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -67,6 +67,7 @@
import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataProvider
import com.android.systemui.media.controls.resume.MediaResumeListener
+import com.android.systemui.media.controls.resume.ResumeMediaBrowser
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.controls.util.MediaFlags
@@ -1431,6 +1432,22 @@
notifyMediaDataLoaded(key = pkg, oldKey = pkg, info = updated)
}
logger.logActiveConvertedToResume(updated.appUid, pkg, updated.instanceId)
+
+ // Limit total number of resume controls
+ val resumeEntries = mediaEntries.filter { (key, data) -> data.resumption }
+ val numResume = resumeEntries.size
+ if (numResume > ResumeMediaBrowser.MAX_RESUMPTION_CONTROLS) {
+ resumeEntries
+ .toList()
+ .sortedBy { (key, data) -> data.lastActive }
+ .subList(0, numResume - ResumeMediaBrowser.MAX_RESUMPTION_CONTROLS)
+ .forEach { (key, data) ->
+ Log.d(TAG, "Removing excess control $key")
+ mediaEntries.remove(key)
+ notifyMediaDataRemoved(key)
+ logger.logMediaRemoved(data.appUid, data.packageName, data.instanceId)
+ }
+ }
}
fun setMediaResumptionEnabled(isEnabled: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
index aa46b14..878962d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
@@ -40,7 +40,7 @@
@VisibleForTesting
val RESUME_MEDIA_TIMEOUT =
- SystemProperties.getLong("debug.sysui.media_timeout_resume", TimeUnit.DAYS.toMillis(3))
+ SystemProperties.getLong("debug.sysui.media_timeout_resume", TimeUnit.DAYS.toMillis(2))
/** Controller responsible for keeping track of playback states and expiring inactive streams. */
@SysUISingleton
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 44e2fbd..5c7d242 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -51,6 +51,7 @@
import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataProvider
import com.android.systemui.media.controls.resume.MediaResumeListener
+import com.android.systemui.media.controls.resume.ResumeMediaBrowser
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
@@ -642,6 +643,46 @@
}
@Test
+ fun testOnNotificationRemoved_withResumption_tooManyPlayers() {
+ // Given the maximum number of resume controls already
+ val desc =
+ MediaDescription.Builder().run {
+ setTitle(SESSION_TITLE)
+ build()
+ }
+ for (i in 0..ResumeMediaBrowser.MAX_RESUMPTION_CONTROLS) {
+ addResumeControlAndLoad(desc, "$i:$PACKAGE_NAME")
+ clock.advanceTime(1000)
+ }
+
+ // And an active, resumable notification
+ whenever(controller.metadata).thenReturn(metadataBuilder.build())
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ assertThat(data.resumption).isFalse()
+ mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
+
+ // When the notification is removed
+ mediaDataManager.onNotificationRemoved(KEY)
+
+ // Then it is converted to resumption
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value.resumption).isTrue()
+ assertThat(mediaDataCaptor.value.isPlaying).isFalse()
+
+ // And the oldest resume control was removed
+ verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"))
+ }
+
+ @Test
fun testAddResumptionControls() {
// WHEN resumption controls are added
val desc =
@@ -1846,7 +1887,10 @@
}
/** Helper function to add a resumption control and capture the resulting MediaData */
- private fun addResumeControlAndLoad(desc: MediaDescription) {
+ private fun addResumeControlAndLoad(
+ desc: MediaDescription,
+ packageName: String = PACKAGE_NAME
+ ) {
mediaDataManager.addResumptionControls(
USER_ID,
desc,
@@ -1854,14 +1898,14 @@
session.sessionToken,
APP_NAME,
pendingIntent,
- PACKAGE_NAME
+ packageName
)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener)
.onMediaDataLoaded(
- eq(PACKAGE_NAME),
+ eq(packageName),
eq(null),
capture(mediaDataCaptor),
eq(true),