diff options
4 files changed, 108 insertions, 51 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 93c2b5ad4d86..dd7db23d668b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -9195,6 +9195,12 @@ public class Notification implements Parcelable * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}. * <p> * + * <p> + * Starting at {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V} the + * {@link Notification#FLAG_NO_CLEAR NO_CLEAR flag} will be set for valid MediaStyle + * notifications. + * <p> + * * To use this style with your Notification, feed it to * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so: * <pre class="prettyprint"> diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index a3c71c2e0218..db69d93583d4 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -541,6 +541,13 @@ public class NotificationManagerService extends SystemService { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2) private static final long NOTIFICATION_LOG_ASSISTANT_CANCEL = 195579280L; + /** + * NO_CLEAR flag will be set for any media notification. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + static final long ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION = 264179692L; + private static final Duration POST_WAKE_LOCK_TIMEOUT = Duration.ofSeconds(30); private IActivityManager mAm; @@ -7189,6 +7196,12 @@ public class NotificationManagerService extends SystemService { + "MEDIA_CONTENT_CONTROL permission"); } } + + // Enforce NO_CLEAR flag on MediaStyle notification for apps with targetSdk >= V. + if (CompatChanges.isChangeEnabled(ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION, + notificationUid)) { + notification.flags |= Notification.FLAG_NO_CLEAR; + } } // Ensure only allowed packages have a substitute app name diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 2dacda0af7f4..1d37f9da8d7a 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -31,6 +31,7 @@ android_test { "androidx.test.rules", "hamcrest-library", "mockito-target-inline-minus-junit4", + "platform-compat-test-rules", "platform-test-annotations", "platformprotosnano", "statsdprotolite", diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 9543a2de1e13..c98d2359b2f9 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -164,6 +164,7 @@ import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.companion.AssociationInfo; import android.companion.ICompanionDeviceManager; +import android.compat.testing.PlatformCompatChangeRule; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentUris; @@ -270,11 +271,15 @@ import com.android.server.wm.WindowManagerInternal; import com.google.android.collect.Lists; import com.google.common.collect.ImmutableList; +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; @@ -317,6 +322,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private final int mUid = Binder.getCallingUid(); private final @UserIdInt int mUserId = UserHandle.getUserId(mUid); + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + private TestableNotificationManagerService mService; private INotificationManager mBinderService; private NotificationManagerInternal mInternalService; @@ -4828,6 +4836,62 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + @EnableCompatChanges({NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION}) + public void testMediaStyle_enforceNoClearFlagEnabled() throws RemoteException { + Notification.MediaStyle style = new Notification.MediaStyle(); + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setStyle(style); + + NotificationRecord posted = createAndPostNotification(nb, "testMediaStyleSetNoClearFlag"); + + assertThat(posted.getFlags() & FLAG_NO_CLEAR).isEqualTo(FLAG_NO_CLEAR); + } + + @Test + @EnableCompatChanges({NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION}) + public void testCustomMediaStyle_enforceNoClearFlagEnabled() throws RemoteException { + Notification.DecoratedMediaCustomViewStyle style = + new Notification.DecoratedMediaCustomViewStyle(); + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setStyle(style); + + NotificationRecord posted = createAndPostNotification(nb, + "testCustomMediaStyleSetNoClearFlag"); + + assertThat(posted.getFlags() & FLAG_NO_CLEAR).isEqualTo(FLAG_NO_CLEAR); + } + + @Test + @DisableCompatChanges(NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION) + public void testMediaStyle_enforceNoClearFlagDisabled() throws RemoteException { + Notification.MediaStyle style = new Notification.MediaStyle(); + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setStyle(style); + + NotificationRecord posted = createAndPostNotification(nb, "testMediaStyleSetNoClearFlag"); + + assertThat(posted.getFlags() & FLAG_NO_CLEAR).isNotEqualTo(FLAG_NO_CLEAR); + } + + @Test + @DisableCompatChanges(NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION) + public void testCustomMediaStyle_enforceNoClearFlagDisabled() throws RemoteException { + Notification.DecoratedMediaCustomViewStyle style = + new Notification.DecoratedMediaCustomViewStyle(); + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setStyle(style); + + NotificationRecord posted = createAndPostNotification(nb, + "testCustomMediaStyleSetNoClearFlag"); + + assertThat(posted.getFlags() & FLAG_NO_CLEAR).isNotEqualTo(FLAG_NO_CLEAR); + } + + @Test public void testMediaStyleRemote_hasPermission() throws RemoteException { String deviceName = "device"; mContext.getTestablePermissions().setPermission( @@ -4838,17 +4902,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel.getId()) .setStyle(style); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, - "testMediaStyleRemoteHasPermission", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - - NotificationRecord posted = mService.findNotificationLocked( - PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + NotificationRecord posted = createAndPostNotification(nb, + "testMediaStyleRemoteHasPermission"); Bundle extras = posted.getNotification().extras; assertTrue(extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); @@ -4866,17 +4921,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel.getId()) .setStyle(style); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, - "testMediaStyleRemoteNoPermission", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - - NotificationRecord posted = mService.findNotificationLocked( - PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + NotificationRecord posted = createAndPostNotification(nb, + "testMediaStyleRemoteNoPermission"); assertFalse(posted.getNotification().extras .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); @@ -4899,17 +4945,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel.getId()) .setStyle(style); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, - "testCustomMediaStyleRemoteNoPermission", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - - NotificationRecord posted = mService.findNotificationLocked( - PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + NotificationRecord posted = createAndPostNotification(nb, + "testCustomMediaStyleRemoteNoPermission"); assertFalse(posted.getNotification().extras .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); @@ -4929,16 +4966,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { Notification.Builder nb = new Notification.Builder(mContext, mTestNotificationChannel.getId()) .addExtras(extras); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, - "testSubstituteAppNamePermission", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - NotificationRecord posted = mService.findNotificationLocked( - PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + NotificationRecord posted = createAndPostNotification(nb, + "testSubstituteAppNameHasPermission"); assertTrue(posted.getNotification().extras .containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)); @@ -4955,16 +4985,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { Notification.Builder nb = new Notification.Builder(mContext, mTestNotificationChannel.getId()) .addExtras(extras); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, - "testSubstituteAppNamePermission", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - NotificationRecord posted = mService.findNotificationLocked( - PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + NotificationRecord posted = createAndPostNotification(nb, + "testSubstituteAppNameNoPermission"); assertFalse(posted.getNotification().extras .containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)); @@ -12656,6 +12679,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(service, times(1)).setDNDMigrationDone(user.id); } + private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName) + throws RemoteException { + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), + nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); + waitForIdle(); + + return mService.findNotificationLocked( + PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + } + private static <T extends Parcelable> T parcelAndUnparcel(T source, Parcelable.Creator<T> creator) { Parcel parcel = Parcel.obtain(); |