Merge "Do not retain album art in NotificationMediaManager if unused" into udc-dev

This commit is contained in:
Beth Thibodeau
2023-04-17 14:12:03 +00:00
committed by Android (Google) Code Review
3 changed files with 166 additions and 23 deletions

View File

@@ -71,6 +71,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Utils;
import com.android.systemui.util.concurrency.DelayableExecutor;
import dagger.Lazy;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -80,8 +82,6 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import dagger.Lazy;
/**
* Handles tasks and state related to media notifications. For example, there is a 'current' media
* notification, which this class keeps track of.
@@ -161,11 +161,48 @@ public class NotificationMediaManager implements Dumpable {
Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
}
mMediaArtworkProcessor.clearCache();
mMediaMetadata = metadata;
mMediaMetadata = cleanMetadata(metadata);
dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
}
};
/**
* If this build is not configured for lockscreen artwork, clear artwork references from the
* metadata to avoid excess memory usage. Otherwise, return as is.
* @param data Original metadata
* @return a copy without artwork data, or original
*/
private MediaMetadata cleanMetadata(MediaMetadata data) {
if (SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
return data;
}
if (data == null) {
return null;
}
if (DEBUG_MEDIA) {
String[] artKeys = new String[] {
MediaMetadata.METADATA_KEY_ART,
MediaMetadata.METADATA_KEY_ALBUM_ART,
MediaMetadata.METADATA_KEY_DISPLAY_ICON,
MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
MediaMetadata.METADATA_KEY_ART_URI,
MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI
};
Log.v(TAG, "DEBUG_MEDIA: removing artwork from metadata");
for (String key: artKeys) {
Log.v(TAG, " " + key + ": " + data.containsKey(key));
}
}
return new MediaMetadata.Builder(data)
.putBitmap(MediaMetadata.METADATA_KEY_ART, null)
.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, null)
.putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, null)
.putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, null)
.putString(MediaMetadata.METADATA_KEY_ART_URI, null)
.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, null)
.build();
}
/**
* Injected constructor. See {@link CentralSurfacesModule}.
*/
@@ -313,6 +350,7 @@ public class NotificationMediaManager implements Dumpable {
return mMediaNotificationKey;
}
@VisibleForTesting
public MediaMetadata getMediaMetadata() {
return mMediaMetadata;
}
@@ -350,7 +388,7 @@ public class NotificationMediaManager implements Dumpable {
* update this manager's internal state.
* @return whether the current MediaMetadata changed (and needs to be announced to listeners).
*/
boolean findPlayingMediaNotification(
private boolean findPlayingMediaNotification(
@NonNull Collection<NotificationEntry> allNotifications) {
boolean metaDataChanged = false;
// Promote the media notification with a controller in 'playing' state, if any.
@@ -383,7 +421,7 @@ public class NotificationMediaManager implements Dumpable {
clearCurrentMediaNotificationSession();
mMediaController = controller;
mMediaController.registerCallback(mMediaListener);
mMediaMetadata = mMediaController.getMetadata();
mMediaMetadata = cleanMetadata(mMediaController.getMetadata());
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: insert listener, found new controller: "
+ mMediaController + ", receive metadata: " + mMediaMetadata);

View File

@@ -75,7 +75,7 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
boolean DEBUG_WAKEUP_DELAY = Compile.IS_DEBUG;
// additional instrumentation for testing purposes; intended to be left on during development
boolean CHATTY = DEBUG;
boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = false;
String ACTION_FAKE_ARTWORK = "fake_artwork";
int FADE_KEYGUARD_START_DELAY = 100;
int FADE_KEYGUARD_DURATION = 300;

View File

@@ -16,11 +16,34 @@
package com.android.systemui.statusbar
import android.app.Notification
import android.app.WallpaperManager
import android.graphics.Bitmap
import android.media.MediaMetadata
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.whenever
import org.junit.After
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import dagger.Lazy
import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,39 +55,121 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
/**
* Temporary test for the lock screen live wallpaper project.
*
* TODO(b/273443374): remove this test
*/
@RunWith(AndroidTestingRunner::class)
@SmallTest
@RunWithLooper
class NotificationMediaManagerTest : SysuiTestCase() {
@Mock private lateinit var notificationMediaManager: NotificationMediaManager
@Mock private lateinit var centralSurfaces: CentralSurfaces
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var visibilityProvider: NotificationVisibilityProvider
@Mock private lateinit var mediaArtworkProcessor: MediaArtworkProcessor
@Mock private lateinit var keyguardBypassController: KeyguardBypassController
@Mock private lateinit var notifPipeline: NotifPipeline
@Mock private lateinit var notifCollection: NotifCollection
@Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var colorExtractor: SysuiColorExtractor
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var wallpaperManager: WallpaperManager
@Mock private lateinit var notificationEntry: NotificationEntry
lateinit var manager: NotificationMediaManager
val clock = FakeSystemClock()
val mainExecutor: DelayableExecutor = FakeExecutor(clock)
@Mock private lateinit var mockManager: NotificationMediaManager
@Mock private lateinit var mockBackDropView: BackDropView
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
doCallRealMethod()
.whenever(notificationMediaManager)
.updateMediaMetaData(anyBoolean(), anyBoolean())
doReturn(mockBackDropView).whenever(notificationMediaManager).backDropView
doCallRealMethod().whenever(mockManager).updateMediaMetaData(anyBoolean(), anyBoolean())
doReturn(mockBackDropView).whenever(mockManager).backDropView
manager =
NotificationMediaManager(
context,
Lazy { Optional.of(centralSurfaces) },
Lazy { notificationShadeWindowController },
visibilityProvider,
mediaArtworkProcessor,
keyguardBypassController,
notifPipeline,
notifCollection,
mainExecutor,
mediaDataManager,
statusBarStateController,
colorExtractor,
keyguardStateController,
dumpManager,
wallpaperManager,
)
}
@After fun tearDown() {}
/** Check that updateMediaMetaData is a no-op with mIsLockscreenLiveWallpaperEnabled = true */
/**
* Check that updateMediaMetaData is a no-op with mIsLockscreenLiveWallpaperEnabled = true
* Temporary test for the lock screen live wallpaper project.
*
* TODO(b/273443374): remove this test
*/
@Test
fun testUpdateMediaMetaDataDisabled() {
notificationMediaManager.mIsLockscreenLiveWallpaperEnabled = true
mockManager.mIsLockscreenLiveWallpaperEnabled = true
for (metaDataChanged in listOf(true, false)) {
for (allowEnterAnimation in listOf(true, false)) {
notificationMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation)
verify(notificationMediaManager, never()).mediaMetadata
mockManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation)
verify(mockManager, never()).mediaMetadata
}
}
}
@Test
fun testMetadataUpdated_doesNotRetainArtwork() {
val artBmp = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
val artUri = "content://example"
val inputMetadata =
MediaMetadata.Builder()
.putBitmap(MediaMetadata.METADATA_KEY_ART, artBmp)
.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, artBmp)
.putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, artBmp)
.putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, artUri)
.putString(MediaMetadata.METADATA_KEY_ART_URI, artUri)
.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, artUri)
.build()
// Create a playing media notification
val state = PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0L, 1f).build()
val session = MediaSession(context, "NotificationMediaManagerTest")
session.setMetadata(inputMetadata)
session.setPlaybackState(state)
val sbn =
SbnBuilder().run {
modifyNotification(context).also {
it.setSmallIcon(android.R.drawable.ic_media_play)
it.setStyle(
Notification.MediaStyle().apply { setMediaSession(session.sessionToken) }
)
}
build()
}
whenever(notificationEntry.sbn).thenReturn(sbn)
val collection = ArrayList<NotificationEntry>()
collection.add(notificationEntry)
whenever(notifPipeline.allNotifs).thenReturn(collection)
// Trigger update in NotificationMediaManager
manager.findAndUpdateMediaNotifications()
// Verify that there is no artwork data retained
val metadata = manager.mediaMetadata
assertThat(metadata.getBitmap(MediaMetadata.METADATA_KEY_ART)).isNull()
assertThat(metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)).isNull()
assertThat(metadata.getBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON)).isNull()
assertThat(metadata.getString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI)).isNull()
assertThat(metadata.getString(MediaMetadata.METADATA_KEY_ART_URI)).isNull()
assertThat(metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI)).isNull()
}
}