Merge "Upgrade NoMan's cancelation reposting logic"
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d0d0f5a..2e4a977 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6243,6 +6243,7 @@
private final int mRank;
private final int mCount;
private final ManagedServiceInfo mListener;
+ private final long mWhen;
CancelNotificationRunnable(final int callingUid, final int callingPid,
final String pkg, final String tag, final int id,
@@ -6262,6 +6263,7 @@
this.mRank = rank;
this.mCount = count;
this.mListener = listener;
+ this.mWhen = System.currentTimeMillis();
}
@Override
@@ -6273,13 +6275,28 @@
}
synchronized (mNotificationLock) {
- // If the notification is currently enqueued, repost this runnable so it has a
- // chance to notify listeners
- if ((findNotificationByListLocked(mEnqueuedNotifications, mPkg, mTag, mId, mUserId))
- != null) {
+ // Check to see if there is a notification in the enqueued list that hasn't had a
+ // chance to post yet.
+ List<NotificationRecord> enqueued = findEnqueuedNotificationsForCriteria(
+ mPkg, mTag, mId, mUserId);
+ boolean repost = false;
+ if (enqueued.size() > 0) {
+ // Found something, let's see what it was
+ repost = true;
+ // If all enqueues happened before this cancel then wait for them to happen,
+ // otherwise we should let this cancel through so the next enqueue happens
+ for (NotificationRecord r : enqueued) {
+ if (r.mUpdateTimeMs > mWhen) {
+ // At least one enqueue was posted after the cancel, so we're invalid
+ return;
+ }
+ }
+ }
+ if (repost) {
mHandler.post(this);
return;
}
+
// Look for the notification in the posted list, since we already checked enqueued.
NotificationRecord r =
findNotificationByListLocked(mNotificationList, mPkg, mTag, mId, mUserId);
@@ -6298,6 +6315,10 @@
if ((r.getNotification().flags & mMustNotHaveFlags) != 0) {
return;
}
+ if (r.getUpdateTimeMs() > mWhen) {
+ // In this case, a post must have slipped by when this runnable reposted
+ return;
+ }
// Bubbled children get to stick around if the summary was manually cancelled
// (user removed) from systemui.
@@ -8220,6 +8241,29 @@
return null;
}
+ /**
+ * There may be multiple records that match your criteria. For instance if there have been
+ * multiple notifications posted which are enqueued for the same pkg, tag, id, userId. This
+ * method will find all of them in the given list
+ * @return
+ */
+ @GuardedBy("mNotificationLock")
+ private List<NotificationRecord> findEnqueuedNotificationsForCriteria(
+ String pkg, String tag, int id, int userId) {
+ final ArrayList<NotificationRecord> records = new ArrayList<>();
+ final int n = mEnqueuedNotifications.size();
+ for (int i = 0; i < n; i++) {
+ NotificationRecord r = mEnqueuedNotifications.get(i);
+ if (notificationMatchesUserId(r, userId)
+ && r.getSbn().getId() == id
+ && TextUtils.equals(r.getSbn().getTag(), tag)
+ && r.getSbn().getPackageName().equals(pkg)) {
+ records.add(r);
+ }
+ }
+ return records;
+ }
+
@GuardedBy("mNotificationLock")
int indexOfNotificationLocked(String key) {
final int N = mNotificationList.size();
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0ada58e..f92e1fc 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -906,6 +906,10 @@
return (int) (now - mInterruptionTimeMs);
}
+ public long getUpdateTimeMs() {
+ return mUpdateTimeMs;
+ }
+
/**
* Set the visibility of the notification.
*/
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 bc33f08..e5ffb4d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1284,6 +1284,27 @@
}
@Test
+ public void testPostCancelPostNotifiesListeners() throws Exception {
+ // WHEN a notification is posted
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(),
+ sbn.getNotification(), sbn.getUserId());
+ // THEN it is canceled
+ mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getUserId());
+ // THEN it is posted again (before the cancel has a chance to finish)
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(),
+ sbn.getNotification(), sbn.getUserId());
+ // THEN the later enqueue isn't swallowed by the cancel. I.e., ordering is respected
+ waitForIdle();
+
+ // The final enqueue made it to the listener instead of being canceled
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifs.length);
+ assertEquals(1, mService.getNotificationRecordCount());
+ }
+
+ @Test
public void testCancelNotificationWhilePostedAndEnqueued() throws Exception {
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelNotificationWhilePostedAndEnqueued", 0,