Merge "Update logic for interruptive notifications" into pi-dev
am: 618033a1c7
Change-Id: I6d081459b209f403129d3786af6ccd4b4d7843c8
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 725f240..327d4fe 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2722,7 +2722,40 @@
* @hide
*/
public static boolean areRemoteViewsChanged(Builder first, Builder second) {
- return !first.usesStandardHeader() || !second.usesStandardHeader();
+ if (!Objects.equals(first.usesStandardHeader(), second.usesStandardHeader())) {
+ return true;
+ }
+
+ if (areRemoteViewsChanged(first.mN.contentView, second.mN.contentView)) {
+ return true;
+ }
+ if (areRemoteViewsChanged(first.mN.bigContentView, second.mN.bigContentView)) {
+ return true;
+ }
+ if (areRemoteViewsChanged(first.mN.headsUpContentView, second.mN.headsUpContentView)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean areRemoteViewsChanged(RemoteViews first, RemoteViews second) {
+ if (first == null && second == null) {
+ return false;
+ }
+ if (first == null && second != null || first != null && second == null) {
+ return true;
+ }
+
+ if (!Objects.equals(first.getLayoutId(), second.getLayoutId())) {
+ return true;
+ }
+
+ if (!Objects.equals(first.getSequenceNumber(), second.getSequenceNumber())) {
+ return true;
+ }
+
+ return false;
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 17f6d5c..32999bb 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
@@ -711,7 +712,7 @@
StatusBarNotification sbn = r.sbn;
cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
sbn.getId(), Notification.FLAG_AUTO_CANCEL,
- Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
+ FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
REASON_CLICK, nv.rank, nv.count, null);
nv.recycle();
reportUserInteraction(r);
@@ -755,7 +756,7 @@
}
}
cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
- Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+ Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
true, userId, REASON_CANCEL, nv.rank, nv.count,null);
nv.recycle();
}
@@ -986,7 +987,7 @@
cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
record.sbn.getPackageName(), record.sbn.getTag(),
record.sbn.getId(), 0,
- Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
+ FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
REASON_TIMEOUT, null);
}
}
@@ -2085,7 +2086,7 @@
// Don't allow client applications to cancel foreground service notis or autobundled
// summaries.
final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
- (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
+ (FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
}
@@ -2100,7 +2101,7 @@
// Calling from user space, don't allow the canceling of actively
// running foreground services.
cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
- pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
+ pkg, null, 0, FLAG_FOREGROUND_SERVICE, true, userId,
REASON_APP_CANCEL_ALL, null);
}
@@ -2686,7 +2687,7 @@
private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
- Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+ Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
true,
userId, REASON_LISTENER_CANCEL, info);
}
@@ -3963,7 +3964,7 @@
// FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
// initially *and* force remove FLAG_FOREGROUND_SERVICE.
sbn.getNotification().flags =
- (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
+ (r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE);
mRankingHelper.sort(mNotificationList);
mListeners.notifyPostedLocked(r, r);
}
@@ -4049,7 +4050,7 @@
final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
r.setIsAppImportanceLocked(mRankingHelper.getIsAppImportanceLocked(pkg, callingUid));
- if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
+ if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0
&& (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
&& (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) {
// Increase the importance of foreground service notifications unless the user had an
@@ -4430,7 +4431,7 @@
mUsageStats.registerUpdatedByApp(r, old);
// Make sure we don't lose the foreground service state.
notification.flags |=
- old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
+ old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
r.isUpdate = true;
r.setInterruptive(isVisuallyInterruptive(old, r));
}
@@ -4439,7 +4440,7 @@
// Ensure if this is a foreground service that the proper additional
// flags are set.
- if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
notification.flags |= Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR;
}
@@ -4507,6 +4508,13 @@
if (oldN.extras == null || newN.extras == null) {
return false;
}
+
+ // Ignore visual interruptions from foreground services because users
+ // consider them one 'session'. Count them for everything else.
+ if (r != null && (r.sbn.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0) {
+ return false;
+ }
+
if (!Objects.equals(oldN.extras.get(Notification.EXTRA_TITLE),
newN.extras.get(Notification.EXTRA_TITLE))) {
return true;
@@ -5810,7 +5818,7 @@
final StatusBarNotification childSbn = childR.sbn;
if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
childR.getGroupKey().equals(parentNotification.getGroupKey())
- && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0
+ && (childR.getFlags() & FLAG_FOREGROUND_SERVICE) == 0
&& (flagChecker == null || flagChecker.apply(childR.getFlags()))) {
EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
childSbn.getTag(), userId, 0, 0, reason, listenerName);
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 eb1c997..72dd8d0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -528,7 +529,7 @@
PKG, new ParceledListSlice(Arrays.asList(channel)));
final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
@@ -557,7 +558,7 @@
mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance());
final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
@@ -601,7 +602,7 @@
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
@@ -759,7 +760,7 @@
@Test
public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
@@ -773,7 +774,7 @@
@Test
public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications("other_pkg_name", sbn.getUserId());
@@ -901,7 +902,7 @@
@Test
public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mInternalService.removeForegroundServiceFlagFromNotification(PKG, sbn.getId(),
@@ -909,14 +910,14 @@
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
- assertEquals(0, notifs[0].getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE);
+ assertEquals(0, notifs[0].getNotification().flags & FLAG_FOREGROUND_SERVICE);
}
@Test
public void testCancelAfterSecondEnqueueDoesNotSpecifyForegroundFlag() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags =
- Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE;
+ Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
sbn.getNotification().flags = Notification.FLAG_ONGOING_EVENT;
@@ -937,7 +938,7 @@
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
@@ -960,7 +961,7 @@
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
@@ -984,7 +985,7 @@
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
@@ -2257,7 +2258,7 @@
NotificationChannel.DEFAULT_CHANNEL_ID)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
- .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
.setPriority(Notification.PRIORITY_MIN);
StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
@@ -2272,7 +2273,7 @@
nb = new Notification.Builder(mContext)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
- .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
.setPriority(Notification.PRIORITY_MIN);
sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
@@ -2670,6 +2671,26 @@
}
@Test
+ public void testVisualDifference_foreground() {
+ Notification.Builder nb1 = new Notification.Builder(mContext, "")
+ .setContentTitle("foo");
+ StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r1 =
+ new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .setContentTitle("bar");
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertFalse(mService.isVisuallyInterruptive(r1, r2));
+ }
+
+ @Test
public void testVisualDifference_diffTitle() {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setContentTitle("foo");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
index d846d21..36ec221 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
@@ -336,6 +336,90 @@
}
@Test
+ public void testRemoteViews_layoutChange() {
+ RemoteViews a = mock(RemoteViews.class);
+ when(a.getLayoutId()).thenReturn(234);
+ RemoteViews b = mock(RemoteViews.class);
+ when(b.getLayoutId()).thenReturn(189);
+
+ Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+ Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+ }
+
+ @Test
+ public void testRemoteViews_layoutSame() {
+ RemoteViews a = mock(RemoteViews.class);
+ when(a.getLayoutId()).thenReturn(234);
+ RemoteViews b = mock(RemoteViews.class);
+ when(b.getLayoutId()).thenReturn(234);
+
+ Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+ Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+ }
+
+ @Test
+ public void testRemoteViews_sequenceChange() {
+ RemoteViews a = mock(RemoteViews.class);
+ when(a.getLayoutId()).thenReturn(234);
+ when(a.getSequenceNumber()).thenReturn(1);
+ RemoteViews b = mock(RemoteViews.class);
+ when(b.getLayoutId()).thenReturn(234);
+ when(b.getSequenceNumber()).thenReturn(2);
+
+ Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+ Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+ }
+
+ @Test
+ public void testRemoteViews_sequenceSame() {
+ RemoteViews a = mock(RemoteViews.class);
+ when(a.getLayoutId()).thenReturn(234);
+ when(a.getSequenceNumber()).thenReturn(1);
+ RemoteViews b = mock(RemoteViews.class);
+ when(b.getLayoutId()).thenReturn(234);
+ when(b.getSequenceNumber()).thenReturn(1);
+
+ Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+ Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+ }
+
+ @Test
public void testActionsDifferent_null() {
Notification n1 = new Notification.Builder(mContext, "test")
.build();