Skip to content
Snippets Groups Projects
Commit 55dce0d9 authored by Michael Mikhail's avatar Michael Mikhail
Browse files

Enforce NO_CLEAR flag on Media Notification

For V+ apps.

Fixes: 264179692
Test: atest NotificationManagerServiceTest
Change-Id: I2da8724833c968becb7e1f122baa7f0a1956c232
parent 57684208
No related branches found
No related tags found
No related merge requests found
......@@ -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">
......
......@@ -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
......
......@@ -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",
......
......@@ -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;
......@@ -4827,6 +4835,62 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertFalse(posted.getNotification().isColorized());
}
 
@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";
......@@ -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();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment