From bf37f65092e1feb877c01fa0062e836ea41bb933 Mon Sep 17 00:00:00 2001 From: WoongKi Min Date: Thu, 25 Aug 2022 17:10:53 +0900 Subject: [PATCH] Prevent FGS notification from exceeding allowed As only the FGS flag is removed by removeForegroundServiceFlagFromNotification, it exceeds the package limit(MAX_PACKAGE_NOTIFICATIONS). And eventually OutOfMemory occurs and the device is reset. When the flag is removed, check in the same way the normal notification to prevent problems. Test: Searching for images in the Chrome browser Test: Download more than 50 images Test: Check NotificationRecord count in Notification dump Change-Id: Iacbc3a3e545f62d15bae5245ebcf6ed47f5c83a8 --- .../NotificationManagerService.java | 68 +++++++++++++++---- .../NotificationManagerServiceTest.java | 56 +++++++++++++++ 2 files changed, 111 insertions(+), 13 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 829b9c7692fe3..a4f10ba05106e 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6275,21 +6275,40 @@ public class NotificationManagerService extends SystemService { checkCallerIsSystem(); mHandler.post(() -> { synchronized (mNotificationLock) { - // strip flag from all enqueued notifications. listeners will be informed - // in post runnable. - List enqueued = findNotificationsByListLocked( - mEnqueuedNotifications, pkg, null, notificationId, userId); - for (int i = 0; i < enqueued.size(); i++) { - removeForegroundServiceFlagLocked(enqueued.get(i)); + int count = getNotificationCount(pkg, userId); + boolean removeFgsNotification = false; + if (count > MAX_PACKAGE_NOTIFICATIONS) { + mUsageStats.registerOverCountQuota(pkg); + removeFgsNotification = true; } + if (removeFgsNotification) { + NotificationRecord r = findNotificationLocked(pkg, null, notificationId, + userId); + if (r != null) { + if (DBG) { + Slog.d(TAG, "Remove FGS flag not allow. Cancel FGS notification"); + } + removeFromNotificationListsLocked(r); + cancelNotificationLocked(r, false, REASON_APP_CANCEL, true, + null, SystemClock.elapsedRealtime()); + } + } else { + // strip flag from all enqueued notifications. listeners will be informed + // in post runnable. + List enqueued = findNotificationsByListLocked( + mEnqueuedNotifications, pkg, null, notificationId, userId); + for (int i = 0; i < enqueued.size(); i++) { + removeForegroundServiceFlagLocked(enqueued.get(i)); + } - // if posted notification exists, strip its flag and tell listeners - NotificationRecord r = findNotificationByListLocked( - mNotificationList, pkg, null, notificationId, userId); - if (r != null) { - removeForegroundServiceFlagLocked(r); - mRankingHelper.sort(mNotificationList); - mListeners.notifyPostedLocked(r, r); + // if posted notification exists, strip its flag and tell listeners + NotificationRecord r = findNotificationByListLocked( + mNotificationList, pkg, null, notificationId, userId); + if (r != null) { + removeForegroundServiceFlagLocked(r); + mRankingHelper.sort(mNotificationList); + mListeners.notifyPostedLocked(r, r); + } } } }); @@ -6967,6 +6986,29 @@ public class NotificationManagerService extends SystemService { return mPermissionHelper.hasPermission(uid); } + private int getNotificationCount(String pkg, int userId) { + int count = 0; + synchronized (mNotificationLock) { + final int numListSize = mNotificationList.size(); + for (int i = 0; i < numListSize; i++) { + final NotificationRecord existing = mNotificationList.get(i); + if (existing.getSbn().getPackageName().equals(pkg) + && existing.getSbn().getUserId() == userId) { + count++; + } + } + final int numEnqSize = mEnqueuedNotifications.size(); + for (int i = 0; i < numEnqSize; i++) { + final NotificationRecord existing = mEnqueuedNotifications.get(i); + if (existing.getSbn().getPackageName().equals(pkg) + && existing.getSbn().getUserId() == userId) { + count++; + } + } + } + return count; + } + protected int getNotificationCount(String pkg, int userId, int excludedId, String excludedTag) { int count = 0; 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 22721a1bcc92a..709c928dad9c2 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -6038,6 +6038,62 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(0, captor.getValue().getNotification().flags); } + @Test + public void testCannotRemoveForegroundFlagWhenOverLimit_enqueued() { + for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) { + Notification n = new Notification.Builder(mContext, "").build(); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, i, null, mUid, 0, + n, UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + mService.addEnqueuedNotification(r); + } + Notification n = new Notification.Builder(mContext, "").build(); + n.flags |= FLAG_FOREGROUND_SERVICE; + + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, + NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS, null, mUid, 0, + n, UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mService.addEnqueuedNotification(r); + + mInternalService.removeForegroundServiceFlagFromNotification( + PKG, r.getSbn().getId(), r.getSbn().getUserId()); + + waitForIdle(); + + assertEquals(NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS, + mService.getNotificationRecordCount()); + } + + @Test + public void testCannotRemoveForegroundFlagWhenOverLimit_posted() { + for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) { + Notification n = new Notification.Builder(mContext, "").build(); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, i, null, mUid, 0, + n, UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + mService.addNotification(r); + } + Notification n = new Notification.Builder(mContext, "").build(); + n.flags |= FLAG_FOREGROUND_SERVICE; + + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, + NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS, null, mUid, 0, + n, UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mService.addNotification(r); + + mInternalService.removeForegroundServiceFlagFromNotification( + PKG, r.getSbn().getId(), r.getSbn().getUserId()); + + waitForIdle(); + + assertEquals(NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS, + mService.getNotificationRecordCount()); + } + @Test public void testAllowForegroundCustomToasts() throws Exception { final String testPackage = "testPackageName";