Loading services/core/java/com/android/server/notification/NotificationManagerService.java +130 −44 Original line number Original line Diff line number Diff line Loading @@ -56,6 +56,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import android.Manifest; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityManagerInternal; Loading Loading @@ -980,6 +981,14 @@ public class NotificationManagerService extends SystemService { void addNotification(NotificationRecord r) { void addNotification(NotificationRecord r) { mNotificationList.add(r); mNotificationList.add(r); mNotificationsByKey.put(r.sbn.getKey(), r); mNotificationsByKey.put(r.sbn.getKey(), r); if (r.sbn.isGroup()) { mSummaryByGroupKey.put(r.getGroupKey(), r); } } @VisibleForTesting void addEnqueuedNotification(NotificationRecord r) { mEnqueuedNotifications.add(r); } } @VisibleForTesting @VisibleForTesting Loading Loading @@ -1016,7 +1025,7 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting @VisibleForTesting void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient, void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient, LightsManager lightsManager, NotificationListeners notificationListeners, LightsManager lightsManager, NotificationListeners notificationListeners, ICompanionDeviceManager companionManager) { ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper) { Resources resources = getContext().getResources(); Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, Loading Loading @@ -1071,21 +1080,7 @@ public class NotificationManagerService extends SystemService { sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED); sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED); } } }); }); mSnoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() { mSnoozeHelper = snoozeHelper; @Override public void repost(int userId, NotificationRecord r) { try { if (DBG) { Slog.d(TAG, "Reposting " + r.getKey()); } enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(), r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(), r.sbn.getNotification(), userId); } catch (Exception e) { Slog.e(TAG, "Cannot un-snooze notification", e); } } }, mUserProfiles); mGroupHelper = new GroupHelper(new GroupHelper.Callback() { mGroupHelper = new GroupHelper(new GroupHelper.Callback() { @Override @Override public void addAutoGroup(String key) { public void addAutoGroup(String key) { Loading Loading @@ -1204,9 +1199,25 @@ public class NotificationManagerService extends SystemService { @Override @Override public void onStart() { public void onStart() { SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() { @Override public void repost(int userId, NotificationRecord r) { try { if (DBG) { Slog.d(TAG, "Reposting " + r.getKey()); } enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(), r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(), r.sbn.getNotification(), userId); } catch (Exception e) { Slog.e(TAG, "Cannot un-snooze notification", e); } } }, mUserProfiles); init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(), init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(), getLocalService(LightsManager.class), new NotificationListeners(), getLocalService(LightsManager.class), new NotificationListeners(), null); null, snoozeHelper); publishBinderService(Context.NOTIFICATION_SERVICE, mService); publishBinderService(Context.NOTIFICATION_SERVICE, mService); publishLocalService(NotificationManagerInternal.class, mInternalService); publishLocalService(NotificationManagerInternal.class, mInternalService); } } Loading Loading @@ -3304,7 +3315,7 @@ public class NotificationManagerService extends SystemService { return false; return false; } } } else if (isCallerInstantApp(pkg)) { } else if (isCallerInstantApp(pkg)) { // Ephemeral apps have some special contraints for notifications. // Ephemeral apps have some special constraints for notifications. // They are not allowed to create new notifications however they are allowed to // They are not allowed to create new notifications however they are allowed to // update notifications created by the system (e.g. a foreground service // update notifications created by the system (e.g. a foreground service // notification). // notification). Loading Loading @@ -3378,6 +3389,76 @@ public class NotificationManagerService extends SystemService { return isBlocked; return isBlocked; } } protected class SnoozeNotificationRunnable implements Runnable { private final String mKey; private final long mDuration; private final String mSnoozeCriterionId; SnoozeNotificationRunnable(String key, long duration, String snoozeCriterionId) { mKey = key; mDuration = duration; mSnoozeCriterionId = snoozeCriterionId; } @Override public void run() { synchronized (mNotificationLock) { final NotificationRecord r = findNotificationByKeyLocked(mKey); if (r != null) { snoozeLocked(r); } } } void snoozeLocked(NotificationRecord r) { if (r.sbn.isGroup()) { final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked( r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId()); if (r.getNotification().isGroupSummary()) { // snooze summary and all children for (int i = 0; i < groupNotifications.size(); i++) { snoozeNotificationLocked(groupNotifications.get(i)); } } else { // if there is a valid summary for this group, and we are snoozing the only // child, also snooze the summary if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) { if (groupNotifications.size() != 2) { snoozeNotificationLocked(r); } else { // snooze summary and the one child for (int i = 0; i < groupNotifications.size(); i++) { snoozeNotificationLocked(groupNotifications.get(i)); } } } else { snoozeNotificationLocked(r); } } } else { // just snooze the one notification snoozeNotificationLocked(r); } } void snoozeNotificationLocked(NotificationRecord r) { MetricsLogger.action(r.getLogMaker() .setCategory(MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsEvent.TYPE_CLOSE) .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA, mSnoozeCriterionId == null ? 0 : 1)); cancelNotificationLocked(r, false, REASON_SNOOZED); updateLightsLocked(); if (mSnoozeCriterionId != null) { mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId); mSnoozeHelper.snooze(r); } else { mSnoozeHelper.snooze(r, mDuration); } savePolicyFile(); } } protected class EnqueueNotificationRunnable implements Runnable { protected class EnqueueNotificationRunnable implements Runnable { private final NotificationRecord r; private final NotificationRecord r; private final int userId; private final int userId; Loading Loading @@ -3412,6 +3493,11 @@ public class NotificationManagerService extends SystemService { // can to avoid extracting signals. // can to avoid extracting signals. handleGroupedNotificationLocked(r, old, callingUid, callingPid); handleGroupedNotificationLocked(r, old, callingUid, callingPid); // if this is a group child, unsnooze parent summary if (n.isGroup() && notification.isGroupChild()) { mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey()); } // This conditional is a dirty hack to limit the logging done on // This conditional is a dirty hack to limit the logging done on // behalf of the download manager without affecting other apps. // behalf of the download manager without affecting other apps. if (!pkg.equals("com.android.providers.downloads") if (!pkg.equals("com.android.providers.downloads") Loading Loading @@ -4394,31 +4480,7 @@ public class NotificationManagerService extends SystemService { snoozeCriterionId, listenerName)); snoozeCriterionId, listenerName)); } } // Needs to post so that it can cancel notifications not yet enqueued. // Needs to post so that it can cancel notifications not yet enqueued. mHandler.post(new Runnable() { mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId)); @Override public void run() { synchronized (mNotificationLock) { final NotificationRecord r = findNotificationByKeyLocked(key); if (r != null) { MetricsLogger.action(r.getLogMaker() .setCategory(MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsEvent.TYPE_CLOSE) .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA, snoozeCriterionId == null ? 0 : 1)); cancelNotificationLocked(r, false, REASON_SNOOZED); updateLightsLocked(); if (snoozeCriterionId != null) { mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, snoozeCriterionId); mSnoozeHelper.snooze(r); } else { mSnoozeHelper.snooze(r, duration); } savePolicyFile(); } } } }); } } void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) { void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) { Loading Loading @@ -4531,6 +4593,30 @@ public class NotificationManagerService extends SystemService { } } } } @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg, String groupKey, int userId) { List<NotificationRecord> records = new ArrayList<>(); records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId)); records.addAll( findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId)); return records; } private @NonNull List<NotificationRecord> findGroupNotificationByListLocked( ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) { List<NotificationRecord> records = new ArrayList<>(); final int len = list.size(); for (int i = 0; i < len; i++) { NotificationRecord r = list.get(i); if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey) && r.sbn.getPackageName().equals(pkg)) { records.add(r); } } return records; } // Searches both enqueued and posted notifications by key. // Searches both enqueued and posted notifications by key. // TODO: need to combine a bunch of these getters with slightly different behavior. // TODO: need to combine a bunch of these getters with slightly different behavior. // TODO: Should enqueuing just add to mNotificationsByKey instead? // TODO: Should enqueuing just add to mNotificationsByKey instead? Loading @@ -4545,7 +4631,7 @@ public class NotificationManagerService extends SystemService { return null; return null; } } private NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) { NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) { NotificationRecord r; NotificationRecord r; if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) { if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) { return r; return r; Loading services/core/java/com/android/server/notification/SnoozeHelper.java +50 −12 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,7 @@ import org.xmlpull.v1.XmlSerializer; import android.annotation.NonNull; import android.annotation.NonNull; import android.app.AlarmManager; import android.app.AlarmManager; import android.app.Notification; import android.app.PendingIntent; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Context; Loading Loading @@ -61,7 +62,6 @@ public class SnoozeHelper { private static final String REPOST_ACTION = SnoozeHelper.class.getSimpleName() + ".EVALUATE"; private static final String REPOST_ACTION = SnoozeHelper.class.getSimpleName() + ".EVALUATE"; private static final int REQUEST_CODE_REPOST = 1; private static final int REQUEST_CODE_REPOST = 1; private static final String REPOST_SCHEME = "repost"; private static final String REPOST_SCHEME = "repost"; private static final String EXTRA_PKG = "pkg"; private static final String EXTRA_KEY = "key"; private static final String EXTRA_KEY = "key"; private static final String EXTRA_USER_ID = "userId"; private static final String EXTRA_USER_ID = "userId"; Loading Loading @@ -98,7 +98,7 @@ public class SnoozeHelper { protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) { protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) { if (mSnoozedNotifications.containsKey(userId) if (mSnoozedNotifications.containsKey(userId) && mSnoozedNotifications.get(userId).containsKey(pkg)) { && mSnoozedNotifications.get(userId).containsKey(pkg)) { mSnoozedNotifications.get(userId).get(pkg).values(); return mSnoozedNotifications.get(userId).get(pkg).values(); } } return Collections.EMPTY_LIST; return Collections.EMPTY_LIST; } } Loading @@ -106,6 +106,7 @@ public class SnoozeHelper { protected @NonNull List<NotificationRecord> getSnoozed() { protected @NonNull List<NotificationRecord> getSnoozed() { List<NotificationRecord> snoozedForUser = new ArrayList<>(); List<NotificationRecord> snoozedForUser = new ArrayList<>(); int[] userIds = mUserProfiles.getCurrentProfileIds(); int[] userIds = mUserProfiles.getCurrentProfileIds(); if (userIds != null) { final int N = userIds.length; final int N = userIds.length; for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) { final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = Loading @@ -120,6 +121,7 @@ public class SnoozeHelper { } } } } } } } return snoozedForUser; return snoozedForUser; } } Loading Loading @@ -281,6 +283,42 @@ public class SnoozeHelper { } } } } protected void repostGroupSummary(String pkg, int userId, String groupKey) { if (mSnoozedNotifications.containsKey(userId)) { ArrayMap<String, ArrayMap<String, NotificationRecord>> keysByPackage = mSnoozedNotifications.get(userId); if (keysByPackage != null && keysByPackage.containsKey(pkg)) { ArrayMap<String, NotificationRecord> recordsByKey = keysByPackage.get(pkg); if (recordsByKey != null) { String groupSummaryKey = null; int N = recordsByKey.size(); for (int i = 0; i < N; i++) { final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i); if (potentialGroupSummary.sbn.isGroup() && potentialGroupSummary.getNotification().isGroupSummary() && groupKey.equals(potentialGroupSummary.getGroupKey())) { groupSummaryKey = potentialGroupSummary.getKey(); break; } } if (groupSummaryKey != null) { NotificationRecord record = recordsByKey.remove(groupSummaryKey); mPackages.remove(groupSummaryKey); mUsers.remove(groupSummaryKey); MetricsLogger.action(record.getLogMaker() .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsProto.MetricsEvent.TYPE_OPEN)); mCallback.repost(userId, record); } } } } } private PendingIntent createPendingIntent(String pkg, String key, int userId) { private PendingIntent createPendingIntent(String pkg, String key, int userId) { return PendingIntent.getBroadcast(mContext, return PendingIntent.getBroadcast(mContext, REQUEST_CODE_REPOST, REQUEST_CODE_REPOST, Loading services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +186 −1 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; import static junit.framework.Assert.fail; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.any; Loading Loading @@ -96,6 +97,7 @@ public class NotificationManagerServiceTest { private NotificationManagerService.NotificationListeners mNotificationListeners; private NotificationManagerService.NotificationListeners mNotificationListeners; private ManagedServices.ManagedServiceInfo mListener; private ManagedServices.ManagedServiceInfo mListener; @Mock private ICompanionDeviceManager mCompanionMgr; @Mock private ICompanionDeviceManager mCompanionMgr; @Mock SnoozeHelper mSnoozeHelper; // Use a Testable subclass so we can simulate calls from the system without failing. // Use a Testable subclass so we can simulate calls from the system without failing. private static class TestableNotificationManagerService extends NotificationManagerService { private static class TestableNotificationManagerService extends NotificationManagerService { Loading Loading @@ -133,7 +135,8 @@ public class NotificationManagerServiceTest { null, new ComponentName(PKG, "test_class"), uid, true, null, 0); null, new ComponentName(PKG, "test_class"), uid, true, null, 0); when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener); when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener); mNotificationManagerService.init(mTestableLooper.getLooper(), mPackageManager, mNotificationManagerService.init(mTestableLooper.getLooper(), mPackageManager, mPackageManagerClient, mockLightsManager, mNotificationListeners, mCompanionMgr); mPackageManagerClient, mockLightsManager, mNotificationListeners, mCompanionMgr, mSnoozeHelper); // Tests call directly into the Binder. // Tests call directly into the Binder. mBinderService = mNotificationManagerService.getBinderService(); mBinderService = mNotificationManagerService.getBinderService(); Loading @@ -147,6 +150,18 @@ public class NotificationManagerServiceTest { mTestableLooper.processAllMessages(); mTestableLooper.processAllMessages(); } } private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id, String groupKey, boolean isSummary) { Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon) .setGroup(groupKey) .setGroupSummary(isSummary); StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id, "tag", uid, 0, nb.build(), new UserHandle(uid), null, 0); return new NotificationRecord(mContext, sbn, channel); } private NotificationRecord generateNotificationRecord(NotificationChannel channel) { private NotificationRecord generateNotificationRecord(NotificationChannel channel) { return generateNotificationRecord(channel, null); return generateNotificationRecord(channel, null); } } Loading Loading @@ -395,6 +410,46 @@ public class NotificationManagerServiceTest { assertEquals(0, notifs[0].getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE); assertEquals(0, notifs[0].getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE); } } @Test public void testFindGroupNotificationsLocked() throws Exception { // make sure the same notification can be found in both lists and returned final NotificationRecord group1 = generateNotificationRecord( mTestNotificationChannel, 1, "group1", true); mNotificationManagerService.addEnqueuedNotification(group1); mNotificationManagerService.addNotification(group1); // should not be returned final NotificationRecord group2 = generateNotificationRecord( mTestNotificationChannel, 2, "group2", true); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, group2.sbn.getId(), group2.sbn.getNotification(), group2.sbn.getUserId()); waitForIdle(); // should not be returned final NotificationRecord nonGroup = generateNotificationRecord( mTestNotificationChannel, 3, null, false); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, nonGroup.sbn.getId(), nonGroup.sbn.getNotification(), nonGroup.sbn.getUserId()); waitForIdle(); // same group, child, should be returned final NotificationRecord group1Child = generateNotificationRecord( mTestNotificationChannel, 4, "group1", false); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, group1Child.sbn.getId(), group1Child.sbn.getNotification(), group1Child.sbn.getUserId()); waitForIdle(); List<NotificationRecord> inGroup1 = mNotificationManagerService.findGroupNotificationsLocked(PKG, group1.getGroupKey(), group1.sbn.getUserId()); assertEquals(3, inGroup1.size()); for (NotificationRecord record : inGroup1) { assertTrue(record.getGroupKey().equals(group1.getGroupKey())); assertTrue(record.sbn.getId() == 1 || record.sbn.getId() == 4); } } @Test @Test public void testTvExtenderChannelOverride_onTv() throws Exception { public void testTvExtenderChannelOverride_onTv() throws Exception { mNotificationManagerService.setIsTelevision(true); mNotificationManagerService.setIsTelevision(true); Loading Loading @@ -701,4 +756,134 @@ public class NotificationManagerServiceTest { assertFalse(mNotificationManagerService.hasCompanionDevice(mListener)); assertFalse(mNotificationManagerService.hasCompanionDevice(mListener)); } } @Test public void testSnoozeRunnable_snoozeNonGrouped() throws Exception { final NotificationRecord nonGrouped = generateNotificationRecord( mTestNotificationChannel, 1, null, false); final NotificationRecord grouped = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mNotificationManagerService.addNotification(grouped); mNotificationManagerService.addNotification(nonGrouped); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mNotificationManagerService.new SnoozeNotificationRunnable( nonGrouped.getKey(), 100, null); snoozeNotificationRunnable.run(); // only snooze the one notification verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong()); } @Test public void testSnoozeRunnable_snoozeSummary_withChildren() throws Exception { final NotificationRecord parent = generateNotificationRecord( mTestNotificationChannel, 1, "group", true); final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); final NotificationRecord child2 = generateNotificationRecord( mTestNotificationChannel, 3, "group", false); mNotificationManagerService.addNotification(parent); mNotificationManagerService.addNotification(child); mNotificationManagerService.addNotification(child2); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mNotificationManagerService.new SnoozeNotificationRunnable( parent.getKey(), 100, null); snoozeNotificationRunnable.run(); // snooze parent and children verify(mSnoozeHelper, times(3)).snooze(any(NotificationRecord.class), anyLong()); } @Test public void testSnoozeRunnable_snoozeGroupChild_fellowChildren() throws Exception { final NotificationRecord parent = generateNotificationRecord( mTestNotificationChannel, 1, "group", true); final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); final NotificationRecord child2 = generateNotificationRecord( mTestNotificationChannel, 3, "group", false); mNotificationManagerService.addNotification(parent); mNotificationManagerService.addNotification(child); mNotificationManagerService.addNotification(child2); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mNotificationManagerService.new SnoozeNotificationRunnable( child2.getKey(), 100, null); snoozeNotificationRunnable.run(); // only snooze the one child verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong()); } @Test public void testSnoozeRunnable_snoozeGroupChild_onlyChildOfSummary() throws Exception { final NotificationRecord parent = generateNotificationRecord( mTestNotificationChannel, 1, "group", true); assertTrue(parent.sbn.getNotification().isGroupSummary()); final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mNotificationManagerService.addNotification(parent); mNotificationManagerService.addNotification(child); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mNotificationManagerService.new SnoozeNotificationRunnable( child.getKey(), 100, null); snoozeNotificationRunnable.run(); // snooze child and summary verify(mSnoozeHelper, times(2)).snooze(any(NotificationRecord.class), anyLong()); } @Test public void testSnoozeRunnable_snoozeGroupChild_noOthersInGroup() throws Exception { final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mNotificationManagerService.addNotification(child); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mNotificationManagerService.new SnoozeNotificationRunnable( child.getKey(), 100, null); snoozeNotificationRunnable.run(); // snooze child only verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong()); } @Test public void testPostGroupChild_unsnoozeParent() throws Exception { final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId()); waitForIdle(); verify(mSnoozeHelper, times(1)).repostGroupSummary( anyString(), anyInt(), eq(child.getGroupKey())); } @Test public void testPostNonGroup_noUnsnoozing() throws Exception { final NotificationRecord record = generateNotificationRecord( mTestNotificationChannel, 2, null, false); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, record.sbn.getId(), record.sbn.getNotification(), record.sbn.getUserId()); waitForIdle(); verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString()); } @Test public void testPostGroupSummary_noUnsnoozing() throws Exception { final NotificationRecord parent = generateNotificationRecord( mTestNotificationChannel, 2, "group", true); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId()); waitForIdle(); verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString()); } } } services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java +44 −1 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +130 −44 Original line number Original line Diff line number Diff line Loading @@ -56,6 +56,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import android.Manifest; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityManagerInternal; Loading Loading @@ -980,6 +981,14 @@ public class NotificationManagerService extends SystemService { void addNotification(NotificationRecord r) { void addNotification(NotificationRecord r) { mNotificationList.add(r); mNotificationList.add(r); mNotificationsByKey.put(r.sbn.getKey(), r); mNotificationsByKey.put(r.sbn.getKey(), r); if (r.sbn.isGroup()) { mSummaryByGroupKey.put(r.getGroupKey(), r); } } @VisibleForTesting void addEnqueuedNotification(NotificationRecord r) { mEnqueuedNotifications.add(r); } } @VisibleForTesting @VisibleForTesting Loading Loading @@ -1016,7 +1025,7 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting @VisibleForTesting void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient, void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient, LightsManager lightsManager, NotificationListeners notificationListeners, LightsManager lightsManager, NotificationListeners notificationListeners, ICompanionDeviceManager companionManager) { ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper) { Resources resources = getContext().getResources(); Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, Loading Loading @@ -1071,21 +1080,7 @@ public class NotificationManagerService extends SystemService { sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED); sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED); } } }); }); mSnoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() { mSnoozeHelper = snoozeHelper; @Override public void repost(int userId, NotificationRecord r) { try { if (DBG) { Slog.d(TAG, "Reposting " + r.getKey()); } enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(), r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(), r.sbn.getNotification(), userId); } catch (Exception e) { Slog.e(TAG, "Cannot un-snooze notification", e); } } }, mUserProfiles); mGroupHelper = new GroupHelper(new GroupHelper.Callback() { mGroupHelper = new GroupHelper(new GroupHelper.Callback() { @Override @Override public void addAutoGroup(String key) { public void addAutoGroup(String key) { Loading Loading @@ -1204,9 +1199,25 @@ public class NotificationManagerService extends SystemService { @Override @Override public void onStart() { public void onStart() { SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() { @Override public void repost(int userId, NotificationRecord r) { try { if (DBG) { Slog.d(TAG, "Reposting " + r.getKey()); } enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(), r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(), r.sbn.getNotification(), userId); } catch (Exception e) { Slog.e(TAG, "Cannot un-snooze notification", e); } } }, mUserProfiles); init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(), init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(), getLocalService(LightsManager.class), new NotificationListeners(), getLocalService(LightsManager.class), new NotificationListeners(), null); null, snoozeHelper); publishBinderService(Context.NOTIFICATION_SERVICE, mService); publishBinderService(Context.NOTIFICATION_SERVICE, mService); publishLocalService(NotificationManagerInternal.class, mInternalService); publishLocalService(NotificationManagerInternal.class, mInternalService); } } Loading Loading @@ -3304,7 +3315,7 @@ public class NotificationManagerService extends SystemService { return false; return false; } } } else if (isCallerInstantApp(pkg)) { } else if (isCallerInstantApp(pkg)) { // Ephemeral apps have some special contraints for notifications. // Ephemeral apps have some special constraints for notifications. // They are not allowed to create new notifications however they are allowed to // They are not allowed to create new notifications however they are allowed to // update notifications created by the system (e.g. a foreground service // update notifications created by the system (e.g. a foreground service // notification). // notification). Loading Loading @@ -3378,6 +3389,76 @@ public class NotificationManagerService extends SystemService { return isBlocked; return isBlocked; } } protected class SnoozeNotificationRunnable implements Runnable { private final String mKey; private final long mDuration; private final String mSnoozeCriterionId; SnoozeNotificationRunnable(String key, long duration, String snoozeCriterionId) { mKey = key; mDuration = duration; mSnoozeCriterionId = snoozeCriterionId; } @Override public void run() { synchronized (mNotificationLock) { final NotificationRecord r = findNotificationByKeyLocked(mKey); if (r != null) { snoozeLocked(r); } } } void snoozeLocked(NotificationRecord r) { if (r.sbn.isGroup()) { final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked( r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId()); if (r.getNotification().isGroupSummary()) { // snooze summary and all children for (int i = 0; i < groupNotifications.size(); i++) { snoozeNotificationLocked(groupNotifications.get(i)); } } else { // if there is a valid summary for this group, and we are snoozing the only // child, also snooze the summary if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) { if (groupNotifications.size() != 2) { snoozeNotificationLocked(r); } else { // snooze summary and the one child for (int i = 0; i < groupNotifications.size(); i++) { snoozeNotificationLocked(groupNotifications.get(i)); } } } else { snoozeNotificationLocked(r); } } } else { // just snooze the one notification snoozeNotificationLocked(r); } } void snoozeNotificationLocked(NotificationRecord r) { MetricsLogger.action(r.getLogMaker() .setCategory(MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsEvent.TYPE_CLOSE) .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA, mSnoozeCriterionId == null ? 0 : 1)); cancelNotificationLocked(r, false, REASON_SNOOZED); updateLightsLocked(); if (mSnoozeCriterionId != null) { mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId); mSnoozeHelper.snooze(r); } else { mSnoozeHelper.snooze(r, mDuration); } savePolicyFile(); } } protected class EnqueueNotificationRunnable implements Runnable { protected class EnqueueNotificationRunnable implements Runnable { private final NotificationRecord r; private final NotificationRecord r; private final int userId; private final int userId; Loading Loading @@ -3412,6 +3493,11 @@ public class NotificationManagerService extends SystemService { // can to avoid extracting signals. // can to avoid extracting signals. handleGroupedNotificationLocked(r, old, callingUid, callingPid); handleGroupedNotificationLocked(r, old, callingUid, callingPid); // if this is a group child, unsnooze parent summary if (n.isGroup() && notification.isGroupChild()) { mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey()); } // This conditional is a dirty hack to limit the logging done on // This conditional is a dirty hack to limit the logging done on // behalf of the download manager without affecting other apps. // behalf of the download manager without affecting other apps. if (!pkg.equals("com.android.providers.downloads") if (!pkg.equals("com.android.providers.downloads") Loading Loading @@ -4394,31 +4480,7 @@ public class NotificationManagerService extends SystemService { snoozeCriterionId, listenerName)); snoozeCriterionId, listenerName)); } } // Needs to post so that it can cancel notifications not yet enqueued. // Needs to post so that it can cancel notifications not yet enqueued. mHandler.post(new Runnable() { mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId)); @Override public void run() { synchronized (mNotificationLock) { final NotificationRecord r = findNotificationByKeyLocked(key); if (r != null) { MetricsLogger.action(r.getLogMaker() .setCategory(MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsEvent.TYPE_CLOSE) .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA, snoozeCriterionId == null ? 0 : 1)); cancelNotificationLocked(r, false, REASON_SNOOZED); updateLightsLocked(); if (snoozeCriterionId != null) { mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, snoozeCriterionId); mSnoozeHelper.snooze(r); } else { mSnoozeHelper.snooze(r, duration); } savePolicyFile(); } } } }); } } void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) { void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) { Loading Loading @@ -4531,6 +4593,30 @@ public class NotificationManagerService extends SystemService { } } } } @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg, String groupKey, int userId) { List<NotificationRecord> records = new ArrayList<>(); records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId)); records.addAll( findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId)); return records; } private @NonNull List<NotificationRecord> findGroupNotificationByListLocked( ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) { List<NotificationRecord> records = new ArrayList<>(); final int len = list.size(); for (int i = 0; i < len; i++) { NotificationRecord r = list.get(i); if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey) && r.sbn.getPackageName().equals(pkg)) { records.add(r); } } return records; } // Searches both enqueued and posted notifications by key. // Searches both enqueued and posted notifications by key. // TODO: need to combine a bunch of these getters with slightly different behavior. // TODO: need to combine a bunch of these getters with slightly different behavior. // TODO: Should enqueuing just add to mNotificationsByKey instead? // TODO: Should enqueuing just add to mNotificationsByKey instead? Loading @@ -4545,7 +4631,7 @@ public class NotificationManagerService extends SystemService { return null; return null; } } private NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) { NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) { NotificationRecord r; NotificationRecord r; if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) { if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) { return r; return r; Loading
services/core/java/com/android/server/notification/SnoozeHelper.java +50 −12 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,7 @@ import org.xmlpull.v1.XmlSerializer; import android.annotation.NonNull; import android.annotation.NonNull; import android.app.AlarmManager; import android.app.AlarmManager; import android.app.Notification; import android.app.PendingIntent; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Context; Loading Loading @@ -61,7 +62,6 @@ public class SnoozeHelper { private static final String REPOST_ACTION = SnoozeHelper.class.getSimpleName() + ".EVALUATE"; private static final String REPOST_ACTION = SnoozeHelper.class.getSimpleName() + ".EVALUATE"; private static final int REQUEST_CODE_REPOST = 1; private static final int REQUEST_CODE_REPOST = 1; private static final String REPOST_SCHEME = "repost"; private static final String REPOST_SCHEME = "repost"; private static final String EXTRA_PKG = "pkg"; private static final String EXTRA_KEY = "key"; private static final String EXTRA_KEY = "key"; private static final String EXTRA_USER_ID = "userId"; private static final String EXTRA_USER_ID = "userId"; Loading Loading @@ -98,7 +98,7 @@ public class SnoozeHelper { protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) { protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) { if (mSnoozedNotifications.containsKey(userId) if (mSnoozedNotifications.containsKey(userId) && mSnoozedNotifications.get(userId).containsKey(pkg)) { && mSnoozedNotifications.get(userId).containsKey(pkg)) { mSnoozedNotifications.get(userId).get(pkg).values(); return mSnoozedNotifications.get(userId).get(pkg).values(); } } return Collections.EMPTY_LIST; return Collections.EMPTY_LIST; } } Loading @@ -106,6 +106,7 @@ public class SnoozeHelper { protected @NonNull List<NotificationRecord> getSnoozed() { protected @NonNull List<NotificationRecord> getSnoozed() { List<NotificationRecord> snoozedForUser = new ArrayList<>(); List<NotificationRecord> snoozedForUser = new ArrayList<>(); int[] userIds = mUserProfiles.getCurrentProfileIds(); int[] userIds = mUserProfiles.getCurrentProfileIds(); if (userIds != null) { final int N = userIds.length; final int N = userIds.length; for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) { final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = Loading @@ -120,6 +121,7 @@ public class SnoozeHelper { } } } } } } } return snoozedForUser; return snoozedForUser; } } Loading Loading @@ -281,6 +283,42 @@ public class SnoozeHelper { } } } } protected void repostGroupSummary(String pkg, int userId, String groupKey) { if (mSnoozedNotifications.containsKey(userId)) { ArrayMap<String, ArrayMap<String, NotificationRecord>> keysByPackage = mSnoozedNotifications.get(userId); if (keysByPackage != null && keysByPackage.containsKey(pkg)) { ArrayMap<String, NotificationRecord> recordsByKey = keysByPackage.get(pkg); if (recordsByKey != null) { String groupSummaryKey = null; int N = recordsByKey.size(); for (int i = 0; i < N; i++) { final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i); if (potentialGroupSummary.sbn.isGroup() && potentialGroupSummary.getNotification().isGroupSummary() && groupKey.equals(potentialGroupSummary.getGroupKey())) { groupSummaryKey = potentialGroupSummary.getKey(); break; } } if (groupSummaryKey != null) { NotificationRecord record = recordsByKey.remove(groupSummaryKey); mPackages.remove(groupSummaryKey); mUsers.remove(groupSummaryKey); MetricsLogger.action(record.getLogMaker() .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsProto.MetricsEvent.TYPE_OPEN)); mCallback.repost(userId, record); } } } } } private PendingIntent createPendingIntent(String pkg, String key, int userId) { private PendingIntent createPendingIntent(String pkg, String key, int userId) { return PendingIntent.getBroadcast(mContext, return PendingIntent.getBroadcast(mContext, REQUEST_CODE_REPOST, REQUEST_CODE_REPOST, Loading
services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +186 −1 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; import static junit.framework.Assert.fail; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.any; Loading Loading @@ -96,6 +97,7 @@ public class NotificationManagerServiceTest { private NotificationManagerService.NotificationListeners mNotificationListeners; private NotificationManagerService.NotificationListeners mNotificationListeners; private ManagedServices.ManagedServiceInfo mListener; private ManagedServices.ManagedServiceInfo mListener; @Mock private ICompanionDeviceManager mCompanionMgr; @Mock private ICompanionDeviceManager mCompanionMgr; @Mock SnoozeHelper mSnoozeHelper; // Use a Testable subclass so we can simulate calls from the system without failing. // Use a Testable subclass so we can simulate calls from the system without failing. private static class TestableNotificationManagerService extends NotificationManagerService { private static class TestableNotificationManagerService extends NotificationManagerService { Loading Loading @@ -133,7 +135,8 @@ public class NotificationManagerServiceTest { null, new ComponentName(PKG, "test_class"), uid, true, null, 0); null, new ComponentName(PKG, "test_class"), uid, true, null, 0); when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener); when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener); mNotificationManagerService.init(mTestableLooper.getLooper(), mPackageManager, mNotificationManagerService.init(mTestableLooper.getLooper(), mPackageManager, mPackageManagerClient, mockLightsManager, mNotificationListeners, mCompanionMgr); mPackageManagerClient, mockLightsManager, mNotificationListeners, mCompanionMgr, mSnoozeHelper); // Tests call directly into the Binder. // Tests call directly into the Binder. mBinderService = mNotificationManagerService.getBinderService(); mBinderService = mNotificationManagerService.getBinderService(); Loading @@ -147,6 +150,18 @@ public class NotificationManagerServiceTest { mTestableLooper.processAllMessages(); mTestableLooper.processAllMessages(); } } private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id, String groupKey, boolean isSummary) { Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon) .setGroup(groupKey) .setGroupSummary(isSummary); StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id, "tag", uid, 0, nb.build(), new UserHandle(uid), null, 0); return new NotificationRecord(mContext, sbn, channel); } private NotificationRecord generateNotificationRecord(NotificationChannel channel) { private NotificationRecord generateNotificationRecord(NotificationChannel channel) { return generateNotificationRecord(channel, null); return generateNotificationRecord(channel, null); } } Loading Loading @@ -395,6 +410,46 @@ public class NotificationManagerServiceTest { assertEquals(0, notifs[0].getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE); assertEquals(0, notifs[0].getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE); } } @Test public void testFindGroupNotificationsLocked() throws Exception { // make sure the same notification can be found in both lists and returned final NotificationRecord group1 = generateNotificationRecord( mTestNotificationChannel, 1, "group1", true); mNotificationManagerService.addEnqueuedNotification(group1); mNotificationManagerService.addNotification(group1); // should not be returned final NotificationRecord group2 = generateNotificationRecord( mTestNotificationChannel, 2, "group2", true); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, group2.sbn.getId(), group2.sbn.getNotification(), group2.sbn.getUserId()); waitForIdle(); // should not be returned final NotificationRecord nonGroup = generateNotificationRecord( mTestNotificationChannel, 3, null, false); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, nonGroup.sbn.getId(), nonGroup.sbn.getNotification(), nonGroup.sbn.getUserId()); waitForIdle(); // same group, child, should be returned final NotificationRecord group1Child = generateNotificationRecord( mTestNotificationChannel, 4, "group1", false); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, group1Child.sbn.getId(), group1Child.sbn.getNotification(), group1Child.sbn.getUserId()); waitForIdle(); List<NotificationRecord> inGroup1 = mNotificationManagerService.findGroupNotificationsLocked(PKG, group1.getGroupKey(), group1.sbn.getUserId()); assertEquals(3, inGroup1.size()); for (NotificationRecord record : inGroup1) { assertTrue(record.getGroupKey().equals(group1.getGroupKey())); assertTrue(record.sbn.getId() == 1 || record.sbn.getId() == 4); } } @Test @Test public void testTvExtenderChannelOverride_onTv() throws Exception { public void testTvExtenderChannelOverride_onTv() throws Exception { mNotificationManagerService.setIsTelevision(true); mNotificationManagerService.setIsTelevision(true); Loading Loading @@ -701,4 +756,134 @@ public class NotificationManagerServiceTest { assertFalse(mNotificationManagerService.hasCompanionDevice(mListener)); assertFalse(mNotificationManagerService.hasCompanionDevice(mListener)); } } @Test public void testSnoozeRunnable_snoozeNonGrouped() throws Exception { final NotificationRecord nonGrouped = generateNotificationRecord( mTestNotificationChannel, 1, null, false); final NotificationRecord grouped = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mNotificationManagerService.addNotification(grouped); mNotificationManagerService.addNotification(nonGrouped); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mNotificationManagerService.new SnoozeNotificationRunnable( nonGrouped.getKey(), 100, null); snoozeNotificationRunnable.run(); // only snooze the one notification verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong()); } @Test public void testSnoozeRunnable_snoozeSummary_withChildren() throws Exception { final NotificationRecord parent = generateNotificationRecord( mTestNotificationChannel, 1, "group", true); final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); final NotificationRecord child2 = generateNotificationRecord( mTestNotificationChannel, 3, "group", false); mNotificationManagerService.addNotification(parent); mNotificationManagerService.addNotification(child); mNotificationManagerService.addNotification(child2); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mNotificationManagerService.new SnoozeNotificationRunnable( parent.getKey(), 100, null); snoozeNotificationRunnable.run(); // snooze parent and children verify(mSnoozeHelper, times(3)).snooze(any(NotificationRecord.class), anyLong()); } @Test public void testSnoozeRunnable_snoozeGroupChild_fellowChildren() throws Exception { final NotificationRecord parent = generateNotificationRecord( mTestNotificationChannel, 1, "group", true); final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); final NotificationRecord child2 = generateNotificationRecord( mTestNotificationChannel, 3, "group", false); mNotificationManagerService.addNotification(parent); mNotificationManagerService.addNotification(child); mNotificationManagerService.addNotification(child2); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mNotificationManagerService.new SnoozeNotificationRunnable( child2.getKey(), 100, null); snoozeNotificationRunnable.run(); // only snooze the one child verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong()); } @Test public void testSnoozeRunnable_snoozeGroupChild_onlyChildOfSummary() throws Exception { final NotificationRecord parent = generateNotificationRecord( mTestNotificationChannel, 1, "group", true); assertTrue(parent.sbn.getNotification().isGroupSummary()); final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mNotificationManagerService.addNotification(parent); mNotificationManagerService.addNotification(child); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mNotificationManagerService.new SnoozeNotificationRunnable( child.getKey(), 100, null); snoozeNotificationRunnable.run(); // snooze child and summary verify(mSnoozeHelper, times(2)).snooze(any(NotificationRecord.class), anyLong()); } @Test public void testSnoozeRunnable_snoozeGroupChild_noOthersInGroup() throws Exception { final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mNotificationManagerService.addNotification(child); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mNotificationManagerService.new SnoozeNotificationRunnable( child.getKey(), 100, null); snoozeNotificationRunnable.run(); // snooze child only verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong()); } @Test public void testPostGroupChild_unsnoozeParent() throws Exception { final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId()); waitForIdle(); verify(mSnoozeHelper, times(1)).repostGroupSummary( anyString(), anyInt(), eq(child.getGroupKey())); } @Test public void testPostNonGroup_noUnsnoozing() throws Exception { final NotificationRecord record = generateNotificationRecord( mTestNotificationChannel, 2, null, false); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, record.sbn.getId(), record.sbn.getNotification(), record.sbn.getUserId()); waitForIdle(); verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString()); } @Test public void testPostGroupSummary_noUnsnoozing() throws Exception { final NotificationRecord parent = generateNotificationRecord( mTestNotificationChannel, 2, "group", true); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId()); waitForIdle(); verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString()); } } }
services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java +44 −1 File changed.Preview size limit exceeded, changes collapsed. Show changes