Merge "Fix two IndexOutOfBoundsException crashes." into oc-dev

am: ded5608f18

Change-Id: I925c6875249e15d2a497b619aba3d05d3644580e
This commit is contained in:
Julia Reynolds
2017-06-16 13:36:32 +00:00
committed by android-build-merger
2 changed files with 75 additions and 20 deletions

View File

@@ -307,11 +307,15 @@ public class NotificationManagerService extends SystemService {
// used as a mutex for access to all active notifications & listeners // used as a mutex for access to all active notifications & listeners
final Object mNotificationLock = new Object(); final Object mNotificationLock = new Object();
@GuardedBy("mNotificationLock")
final ArrayList<NotificationRecord> mNotificationList = final ArrayList<NotificationRecord> mNotificationList =
new ArrayList<NotificationRecord>(); new ArrayList<NotificationRecord>();
@GuardedBy("mNotificationLock")
final ArrayMap<String, NotificationRecord> mNotificationsByKey = final ArrayMap<String, NotificationRecord> mNotificationsByKey =
new ArrayMap<String, NotificationRecord>(); new ArrayMap<String, NotificationRecord>();
@GuardedBy("mNotificationLock")
final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>(); final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
@GuardedBy("mNotificationLock")
final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>(); final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>(); final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
@@ -2806,7 +2810,8 @@ public class NotificationManagerService extends SystemService {
// Clear summary. // Clear summary.
final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg)); final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
if (removed != null) { if (removed != null) {
cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED); boolean wasPosted = removeFromNotificationListsLocked(removed);
cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted);
} }
} }
} }
@@ -3420,7 +3425,8 @@ public class NotificationManagerService extends SystemService {
.setType(MetricsEvent.TYPE_CLOSE) .setType(MetricsEvent.TYPE_CLOSE)
.addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA, .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
mSnoozeCriterionId == null ? 0 : 1)); mSnoozeCriterionId == null ? 0 : 1));
cancelNotificationLocked(r, false, REASON_SNOOZED); boolean wasPosted = removeFromNotificationListsLocked(r);
cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted);
updateLightsLocked(); updateLightsLocked();
if (mSnoozeCriterionId != null) { if (mSnoozeCriterionId != null) {
mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId); mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
@@ -4206,15 +4212,18 @@ public class NotificationManagerService extends SystemService {
manager.sendAccessibilityEvent(event); manager.sendAccessibilityEvent(event);
} }
/**
* Removes all NotificationsRecords with the same key as the given notification record
* from both lists. Do not call this method while iterating over either list.
*/
@GuardedBy("mNotificationLock") @GuardedBy("mNotificationLock")
private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { private boolean removeFromNotificationListsLocked(NotificationRecord r) {
final String canceledKey = r.getKey(); // Remove from both lists, either list could have a separate Record for what is
// effectively the same notification.
// Remove from both lists, either list could have a separate Record for what is effectively
// the same notification.
boolean wasPosted = false; boolean wasPosted = false;
NotificationRecord recordInList = null; NotificationRecord recordInList = null;
if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey())) != null) { if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
!= null) {
mNotificationList.remove(recordInList); mNotificationList.remove(recordInList);
mNotificationsByKey.remove(recordInList.sbn.getKey()); mNotificationsByKey.remove(recordInList.sbn.getKey());
wasPosted = true; wasPosted = true;
@@ -4223,6 +4232,13 @@ public class NotificationManagerService extends SystemService {
!= null) { != null) {
mEnqueuedNotifications.remove(recordInList); mEnqueuedNotifications.remove(recordInList);
} }
return wasPosted;
}
@GuardedBy("mNotificationLock")
private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
boolean wasPosted) {
final String canceledKey = r.getKey();
// Record caller. // Record caller.
recordCallerLocked(r); recordCallerLocked(r);
@@ -4363,7 +4379,8 @@ public class NotificationManagerService extends SystemService {
} }
// Cancel the notification. // Cancel the notification.
cancelNotificationLocked(r, sendDelete, reason); boolean wasPosted = removeFromNotificationListsLocked(r);
cancelNotificationLocked(r, sendDelete, reason, wasPosted);
cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName, cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
sendDelete); sendDelete);
updateLightsLocked(); updateLightsLocked();
@@ -4440,11 +4457,11 @@ public class NotificationManagerService extends SystemService {
cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid, cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason, false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
listenerName); listenerName, true /* wasPosted */);
cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid, cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
flagChecker, false /*includeCurrentProfiles*/, userId, flagChecker, false /*includeCurrentProfiles*/, userId,
false /*sendDelete*/, reason, listenerName); false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
mSnoozeHelper.cancel(userId, pkg); mSnoozeHelper.cancel(userId, pkg);
} }
} }
@@ -4460,7 +4477,7 @@ public class NotificationManagerService extends SystemService {
private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList, private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch, int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId, String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
boolean sendDelete, int reason, String listenerName) { boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
ArrayList<NotificationRecord> canceledNotifications = null; ArrayList<NotificationRecord> canceledNotifications = null;
for (int i = notificationList.size() - 1; i >= 0; --i) { for (int i = notificationList.size() - 1; i >= 0; --i) {
NotificationRecord r = notificationList.get(i); NotificationRecord r = notificationList.get(i);
@@ -4488,8 +4505,9 @@ public class NotificationManagerService extends SystemService {
if (canceledNotifications == null) { if (canceledNotifications == null) {
canceledNotifications = new ArrayList<>(); canceledNotifications = new ArrayList<>();
} }
notificationList.remove(i);
canceledNotifications.add(r); canceledNotifications.add(r);
cancelNotificationLocked(r, sendDelete, reason); cancelNotificationLocked(r, sendDelete, reason, wasPosted);
} }
if (canceledNotifications != null) { if (canceledNotifications != null) {
final int M = canceledNotifications.size(); final int M = canceledNotifications.size();
@@ -4548,11 +4566,11 @@ public class NotificationManagerService extends SystemService {
cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid, cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker, null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
includeCurrentProfiles, userId, true /*sendDelete*/, reason, includeCurrentProfiles, userId, true /*sendDelete*/, reason,
listenerName); listenerName, true);
cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid, cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null, callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/, flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
reason, listenerName); reason, listenerName, false);
mSnoozeHelper.cancel(userId, includeCurrentProfiles); mSnoozeHelper.cancel(userId, includeCurrentProfiles);
} }
} }
@@ -4569,7 +4587,6 @@ public class NotificationManagerService extends SystemService {
} }
String pkg = r.sbn.getPackageName(); String pkg = r.sbn.getPackageName();
int userId = r.getUserId();
if (pkg == null) { if (pkg == null) {
if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey()); if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
@@ -4577,15 +4594,15 @@ public class NotificationManagerService extends SystemService {
} }
cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName, cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
sendDelete); sendDelete, true);
cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid, cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
listenerName, sendDelete); listenerName, sendDelete, false);
} }
@GuardedBy("mNotificationLock") @GuardedBy("mNotificationLock")
private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList, private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
NotificationRecord parentNotification, int callingUid, int callingPid, NotificationRecord parentNotification, int callingUid, int callingPid,
String listenerName, boolean sendDelete) { String listenerName, boolean sendDelete, boolean wasPosted) {
final String pkg = parentNotification.sbn.getPackageName(); final String pkg = parentNotification.sbn.getPackageName();
final int userId = parentNotification.getUserId(); final int userId = parentNotification.getUserId();
final int reason = REASON_GROUP_SUMMARY_CANCELED; final int reason = REASON_GROUP_SUMMARY_CANCELED;
@@ -4597,7 +4614,8 @@ public class NotificationManagerService extends SystemService {
&& (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) { && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(), EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
childSbn.getTag(), userId, 0, 0, reason, listenerName); childSbn.getTag(), userId, 0, 0, reason, listenerName);
cancelNotificationLocked(childR, sendDelete, reason); notificationList.remove(i);
cancelNotificationLocked(childR, sendDelete, reason, wasPosted);
} }
} }
} }

View File

@@ -358,6 +358,43 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
assertEquals(0, notifs.length); assertEquals(0, notifs.length);
} }
@Test
public void testCancelAllNotificationsMultipleEnqueuedDoesNotCrash() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
for (int i = 0; i < 10; i++) {
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
}
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
waitForIdle();
}
@Test
public void testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash() throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group1", true);
final NotificationRecord parentAsChild = generateNotificationRecord(
mTestNotificationChannel, 1, "group1", false);
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group1", false);
// fully post parent notification
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
waitForIdle();
// enqueue the child several times
for (int i = 0; i < 10; i++) {
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
}
// make the parent a child, which will cancel the child notification
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(),
parentAsChild.sbn.getUserId());
waitForIdle();
}
@Test @Test
public void testCancelAllNotifications_IgnoreForegroundService() throws Exception { public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn; final StatusBarNotification sbn = generateNotificationRecord(null).sbn;