Loading core/java/android/app/INotificationManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -162,6 +162,7 @@ interface INotificationManager void applyAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment); void applyAdjustmentsFromAssistant(in INotificationListener token, in List<Adjustment> adjustments); void unsnoozeNotificationFromAssistant(in INotificationListener token, String key); void unsnoozeNotificationFromSystemListener(in INotificationListener token, String key); ComponentName getEffectsSuppressor(); boolean matchesCallFilter(in Bundle extras); Loading services/core/java/com/android/server/notification/NotificationManagerService.java +23 −0 Original line number Diff line number Diff line Loading @@ -3978,6 +3978,29 @@ public class NotificationManagerService extends SystemService { } } /** * Allows the notification assistant to un-snooze a single notification. * * @param token The binder for the listener, to check that the caller is allowed */ @Override public void unsnoozeNotificationFromSystemListener(INotificationListener token, String key) { long identity = Binder.clearCallingIdentity(); try { synchronized (mNotificationLock) { final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); if (!info.isSystem) { throw new SecurityException("Not allowed to unsnooze before deadline"); } unsnoozeNotificationInt(key, info); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Allow an INotificationListener to simulate clearing (dismissing) a single notification. * Loading services/core/java/com/android/server/notification/SnoozeHelper.java +170 −228 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.notification; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; Loading @@ -42,7 +43,9 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; import java.sql.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; Loading Loading @@ -77,26 +80,26 @@ public class SnoozeHelper { private static final String REPOST_ACTION = SnoozeHelper.class.getSimpleName() + ".EVALUATE"; private static final int REQUEST_CODE_REPOST = 1; private static final String REPOST_SCHEME = "repost"; private static final String EXTRA_KEY = "key"; static final String EXTRA_KEY = "key"; private static final String EXTRA_USER_ID = "userId"; private final Context mContext; private AlarmManager mAm; private final ManagedServices.UserProfiles mUserProfiles; // User id : package name : notification key : record. private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>> // User id | package name : notification key : record. private ArrayMap<String, ArrayMap<String, NotificationRecord>> mSnoozedNotifications = new ArrayMap<>(); // User id : package name : notification key : time-milliseconds . // User id | package name : notification key : time-milliseconds . // This member stores persisted snoozed notification trigger times. it persists through reboots // It should have the notifications that haven't expired or re-posted yet private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, Long>>> private final ArrayMap<String, ArrayMap<String, Long>> mPersistedSnoozedNotifications = new ArrayMap<>(); // User id : package name : notification key : creation ID . // User id | package name : notification key : creation ID . // This member stores persisted snoozed notification trigger context for the assistant // it persists through reboots. // It should have the notifications that haven't expired or re-posted yet private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, String>>> private final ArrayMap<String, ArrayMap<String, String>> mPersistedSnoozedNotificationsWithContext = new ArrayMap<>(); // notification key : package. private ArrayMap<String, String> mPackages = new ArrayMap<>(); Loading @@ -115,6 +118,10 @@ public class SnoozeHelper { mUserProfiles = userProfiles; } private String getPkgKey(@UserIdInt int userId, String pkg) { return userId + "|" + pkg; } void cleanupPersistedContext(String key){ int userId = mUsers.get(key); String pkg = mPackages.get(key); Loading Loading @@ -144,15 +151,13 @@ public class SnoozeHelper { } protected boolean isSnoozed(int userId, String pkg, String key) { return mSnoozedNotifications.containsKey(userId) && mSnoozedNotifications.get(userId).containsKey(pkg) && mSnoozedNotifications.get(userId).get(pkg).containsKey(key); return mSnoozedNotifications.containsKey(getPkgKey(userId, pkg)) && mSnoozedNotifications.get(getPkgKey(userId, pkg)).containsKey(key); } protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) { if (mSnoozedNotifications.containsKey(userId) && mSnoozedNotifications.get(userId).containsKey(pkg)) { return mSnoozedNotifications.get(userId).get(pkg).values(); if (mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))) { return mSnoozedNotifications.get(getPkgKey(userId, pkg)).values(); } return Collections.EMPTY_LIST; } Loading @@ -161,14 +166,14 @@ public class SnoozeHelper { ArrayList<NotificationRecord> getNotifications(String pkg, String groupKey, Integer userId) { ArrayList<NotificationRecord> records = new ArrayList<>(); if (mSnoozedNotifications.containsKey(userId) && mSnoozedNotifications.get(userId).containsKey(pkg)) { ArrayMap<String, NotificationRecord> packages = mSnoozedNotifications.get(userId).get(pkg); for (int i = 0; i < packages.size(); i++) { String currentGroupKey = packages.valueAt(i).getSbn().getGroup(); ArrayMap<String, NotificationRecord> allRecords = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (allRecords != null) { for (int i = 0; i < allRecords.size(); i++) { NotificationRecord r = allRecords.valueAt(i); String currentGroupKey = r.getSbn().getGroup(); if (currentGroupKey.equals(groupKey)) { records.add(packages.valueAt(i)); records.add(r); } } } Loading @@ -176,47 +181,30 @@ public class SnoozeHelper { } protected NotificationRecord getNotification(String key) { List<NotificationRecord> snoozedForUser = new ArrayList<>(); IntArray userIds = mUserProfiles.getCurrentProfileIds(); if (userIds != null) { final int userIdsSize = userIds.size(); for (int i = 0; i < userIdsSize; i++) { final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = mSnoozedNotifications.get(userIds.get(i)); if (snoozedPkgs != null) { final int snoozedPkgsSize = snoozedPkgs.size(); for (int j = 0; j < snoozedPkgsSize; j++) { final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j); if (records != null) { return records.get(key); } } } } if (!mUsers.containsKey(key) || !mPackages.containsKey(key)) { Slog.w(TAG, "Snoozed data sets no longer agree for " + key); return null; } int userId = mUsers.get(key); String pkg = mPackages.get(key); ArrayMap<String, NotificationRecord> snoozed = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (snoozed == null) { return null; } return snoozed.get(key); } protected @NonNull List<NotificationRecord> getSnoozed() { List<NotificationRecord> snoozedForUser = new ArrayList<>(); IntArray userIds = mUserProfiles.getCurrentProfileIds(); if (userIds != null) { final int N = userIds.size(); for (int i = 0; i < N; i++) { final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = mSnoozedNotifications.get(userIds.get(i)); if (snoozedPkgs != null) { final int M = snoozedPkgs.size(); for (int j = 0; j < M; j++) { final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j); if (records != null) { snoozedForUser.addAll(records.values()); // caller filters records based on the current user profiles and listener access, so just // return everything List<NotificationRecord> snoozed= new ArrayList<>(); for (String userPkgKey : mSnoozedNotifications.keySet()) { ArrayMap<String, NotificationRecord> snoozedRecords = mSnoozedNotifications.get(userPkgKey); snoozed.addAll(snoozedRecords.values()); } } } } } return snoozedForUser; return snoozed; } /** Loading Loading @@ -261,50 +249,34 @@ public class SnoozeHelper { } private <T> void storeRecord(String pkg, String key, Integer userId, ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, T object) { ArrayMap<String, ArrayMap<String, T>> targets, T object) { ArrayMap<String, ArrayMap<String, T>> records = targets.get(userId); if (records == null) { records = new ArrayMap<>(); } ArrayMap<String, T> pkgRecords = records.get(pkg); if (pkgRecords == null) { pkgRecords = new ArrayMap<>(); ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg)); if (keyToValue == null) { keyToValue = new ArrayMap<>(); } pkgRecords.put(key, object); records.put(pkg, pkgRecords); targets.put(userId, records); keyToValue.put(key, object); targets.put(getPkgKey(userId, pkg), keyToValue); } private <T> T removeRecord(String pkg, String key, Integer userId, ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets) { ArrayMap<String, ArrayMap<String, T>> targets) { T object = null; ArrayMap<String, ArrayMap<String, T>> records = targets.get(userId); if (records == null) { return null; } ArrayMap<String, T> pkgRecords = records.get(pkg); if (pkgRecords == null) { ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg)); if (keyToValue == null) { return null; } object = pkgRecords.remove(key); if (pkgRecords.size() == 0) { records.remove(pkg); } if (records.size() == 0) { targets.remove(userId); object = keyToValue.remove(key); if (keyToValue.size() == 0) { targets.remove(getPkgKey(userId, pkg)); } return object; } protected boolean cancel(int userId, String pkg, String tag, int id) { if (mSnoozedNotifications.containsKey(userId)) { ArrayMap<String, NotificationRecord> recordsForPkg = mSnoozedNotifications.get(userId).get(pkg); mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (recordsForPkg != null) { final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet(); for (Map.Entry<String, NotificationRecord> record : records) { Loading @@ -315,66 +287,50 @@ public class SnoozeHelper { } } } } return false; } protected boolean cancel(int userId, boolean includeCurrentProfiles) { int[] userIds = {userId}; if (includeCurrentProfiles) { userIds = mUserProfiles.getCurrentProfileIds().toArray(); } final int N = userIds.length; for (int i = 0; i < N; i++) { final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = mSnoozedNotifications.get(userIds[i]); if (snoozedPkgs != null) { final int M = snoozedPkgs.size(); for (int j = 0; j < M; j++) { final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j); if (records != null) { int P = records.size(); for (int k = 0; k < P; k++) { records.valueAt(k).isCanceled = true; protected void cancel(int userId, boolean includeCurrentProfiles) { if (mSnoozedNotifications.size() == 0) { return; } IntArray userIds = new IntArray(); userIds.add(userId); if (includeCurrentProfiles) { userIds = mUserProfiles.getCurrentProfileIds(); } for (ArrayMap<String, NotificationRecord> snoozedRecords : mSnoozedNotifications.values()) { for (NotificationRecord r : snoozedRecords.values()) { if (userIds.binarySearch(r.getUserId()) >= 0) { r.isCanceled = true; } return true; } } return false; } protected boolean cancel(int userId, String pkg) { if (mSnoozedNotifications.containsKey(userId)) { if (mSnoozedNotifications.get(userId).containsKey(pkg)) { ArrayMap<String, NotificationRecord> records = mSnoozedNotifications.get(userId).get(pkg); mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { return false; } int N = records.size(); for (int i = 0; i < N; i++) { records.valueAt(i).isCanceled = true; } return true; } } return false; } /** * Updates the notification record so the most up to date information is shown on re-post. */ protected void update(int userId, NotificationRecord record) { ArrayMap<String, ArrayMap<String, NotificationRecord>> records = mSnoozedNotifications.get(userId); ArrayMap<String, NotificationRecord> records = mSnoozedNotifications.get(getPkgKey(userId, record.getSbn().getPackageName())); if (records == null) { return; } ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.getSbn().getPackageName()); if (pkgRecords == null) { return; } NotificationRecord existing = pkgRecords.get(record.getKey()); pkgRecords.put(record.getKey(), record); records.put(record.getKey(), record); } protected void repost(String key) { Loading @@ -386,20 +342,18 @@ public class SnoozeHelper { protected void repost(String key, int userId) { final String pkg = mPackages.remove(key); ArrayMap<String, ArrayMap<String, NotificationRecord>> records = mSnoozedNotifications.get(userId); ArrayMap<String, NotificationRecord> records = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { return; } ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg); if (pkgRecords == null) { return; } final NotificationRecord record = pkgRecords.remove(key); final NotificationRecord record = records.remove(key); mPackages.remove(key); mUsers.remove(key); if (record != null && !record.isCanceled) { final PendingIntent pi = createPendingIntent(pkg, record.getKey(), userId); mAm.cancel(pi); MetricsLogger.action(record.getLogMaker() .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsProto.MetricsEvent.TYPE_OPEN)); Loading @@ -408,14 +362,12 @@ 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); ArrayMap<String, NotificationRecord> recordsByKey = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (recordsByKey == null) { return; } if (recordsByKey != null) { String groupSummaryKey = null; int N = recordsByKey.size(); for (int i = 0; i < N; i++) { Loading @@ -441,22 +393,15 @@ public class SnoozeHelper { } } } } } } protected void clearData(int userId, String pkg) { ArrayMap<String, ArrayMap<String, NotificationRecord>> records = mSnoozedNotifications.get(userId); ArrayMap<String, NotificationRecord> records = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { return; } ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg); if (pkgRecords == null) { return; } for (int i = pkgRecords.size() - 1; i >= 0; i--) { final NotificationRecord r = pkgRecords.removeAt(i); for (int i = records.size() - 1; i >= 0; i--) { final NotificationRecord r = records.removeAt(i); if (r != null) { mPackages.remove(r.getKey()); mUsers.remove(r.getKey()); Loading Loading @@ -495,16 +440,12 @@ public class SnoozeHelper { public void dump(PrintWriter pw, NotificationManagerService.DumpFilter filter) { pw.println("\n Snoozed notifications:"); for (int userId : mSnoozedNotifications.keySet()) { pw.print(INDENT); pw.println("user: " + userId); ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = mSnoozedNotifications.get(userId); for (String pkg : snoozedPkgs.keySet()) { pw.print(INDENT); for (String userPkgKey : mSnoozedNotifications.keySet()) { pw.print(INDENT); pw.println("package: " + pkg); Set<String> snoozedKeys = snoozedPkgs.get(pkg).keySet(); pw.println("key: " + userPkgKey); ArrayMap<String, NotificationRecord> snoozedRecords = mSnoozedNotifications.get(userPkgKey); Set<String> snoozedKeys = snoozedRecords.keySet(); for (String key : snoozedKeys) { pw.print(INDENT); pw.print(INDENT); Loading @@ -512,6 +453,24 @@ public class SnoozeHelper { pw.println(key); } } pw.println("\n Pending snoozed notifications"); for (String userPkgKey : mPersistedSnoozedNotifications.keySet()) { pw.print(INDENT); pw.println("key: " + userPkgKey); ArrayMap<String, Long> snoozedRecords = mPersistedSnoozedNotifications.get(userPkgKey); if (snoozedRecords == null) { continue; } Set<String> snoozedKeys = snoozedRecords.keySet(); for (String key : snoozedKeys) { pw.print(INDENT); pw.print(INDENT); pw.print(INDENT); pw.print(key); pw.print(INDENT); pw.println(snoozedRecords.get(key)); } } } Loading @@ -538,26 +497,18 @@ public class SnoozeHelper { void insert(T t) throws IOException; } private <T> void writeXml(XmlSerializer out, ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, String tag, ArrayMap<String, ArrayMap<String, T>> targets, String tag, Inserter<T> attributeInserter) throws IOException { synchronized (targets) { final int M = targets.size(); for (int i = 0; i < M; i++) { final ArrayMap<String, ArrayMap<String, T>> packages = targets.valueAt(i); if (packages == null) { continue; } final int N = packages.size(); for (int j = 0; j < N; j++) { final ArrayMap<String, T> keyToValue = packages.valueAt(j); if (keyToValue == null) { continue; } final int O = keyToValue.size(); for (int k = 0; k < O; k++) { T value = keyToValue.valueAt(k); String userIdPkgKey = targets.keyAt(i); // T is a String (snoozed until context) or Long (snoozed until time) ArrayMap<String, T> keyToValue = targets.valueAt(i); for (int j = 0; j < keyToValue.size(); j++) { String key = keyToValue.keyAt(j); T value = keyToValue.valueAt(j); out.startTag(null, tag); Loading @@ -565,14 +516,15 @@ public class SnoozeHelper { out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, XML_SNOOZED_NOTIFICATION_VERSION); out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, keyToValue.keyAt(k)); out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, packages.keyAt(j)); out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key); String pkg = mPackages.get(key); int userId = mUsers.get(key); out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg); out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID, targets.keyAt(i).toString()); String.valueOf(userId)); out.endTag(null, tag); } } } } Loading Loading @@ -606,7 +558,6 @@ public class SnoozeHelper { } scheduleRepost(pkg, key, userId, time - System.currentTimeMillis()); } continue; } if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) { final String creationId = parser.getAttributeValue( Loading @@ -615,18 +566,9 @@ public class SnoozeHelper { storeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext, creationId); } continue; } } catch (Exception e) { //we dont cre if it is a number format exception or a null pointer exception. //we just want to debug it and continue with our lives if (DEBUG) { Slog.d(TAG, "Exception in reading snooze data from policy xml: " + e.getMessage()); } Slog.e(TAG, "Exception in reading snooze data from policy xml", e); } } } Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +27 −0 Original line number Diff line number Diff line Loading @@ -2621,6 +2621,33 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString()); } @Test public void testSystemNotificationListenerCanUnsnooze() throws Exception { final NotificationRecord nr = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mBinderService.enqueueNotificationWithTag(PKG, PKG, "testSystemNotificationListenerCanUnsnooze", nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); waitForIdle(); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( nr.getKey(), 100, null); snoozeNotificationRunnable.run(); ManagedServices.ManagedServiceInfo listener = mListeners.new ManagedServiceInfo( null, new ComponentName(PKG, "test_class"), mUid, true, null, 0); listener.isSystem = true; when(mListeners.checkServiceTokenLocked(any())).thenReturn(listener); mBinderService.unsnoozeNotificationFromSystemListener(null, nr.getKey()); waitForIdle(); StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); assertEquals(1, notifs.length); assertNotNull(notifs[0].getKey());//mService.getNotificationRecord(nr.getSbn().getKey())); } @Test public void testSetListenerAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); Loading Loading
core/java/android/app/INotificationManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -162,6 +162,7 @@ interface INotificationManager void applyAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment); void applyAdjustmentsFromAssistant(in INotificationListener token, in List<Adjustment> adjustments); void unsnoozeNotificationFromAssistant(in INotificationListener token, String key); void unsnoozeNotificationFromSystemListener(in INotificationListener token, String key); ComponentName getEffectsSuppressor(); boolean matchesCallFilter(in Bundle extras); Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +23 −0 Original line number Diff line number Diff line Loading @@ -3978,6 +3978,29 @@ public class NotificationManagerService extends SystemService { } } /** * Allows the notification assistant to un-snooze a single notification. * * @param token The binder for the listener, to check that the caller is allowed */ @Override public void unsnoozeNotificationFromSystemListener(INotificationListener token, String key) { long identity = Binder.clearCallingIdentity(); try { synchronized (mNotificationLock) { final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); if (!info.isSystem) { throw new SecurityException("Not allowed to unsnooze before deadline"); } unsnoozeNotificationInt(key, info); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Allow an INotificationListener to simulate clearing (dismissing) a single notification. * Loading
services/core/java/com/android/server/notification/SnoozeHelper.java +170 −228 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.notification; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; Loading @@ -42,7 +43,9 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; import java.sql.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; Loading Loading @@ -77,26 +80,26 @@ public class SnoozeHelper { private static final String REPOST_ACTION = SnoozeHelper.class.getSimpleName() + ".EVALUATE"; private static final int REQUEST_CODE_REPOST = 1; private static final String REPOST_SCHEME = "repost"; private static final String EXTRA_KEY = "key"; static final String EXTRA_KEY = "key"; private static final String EXTRA_USER_ID = "userId"; private final Context mContext; private AlarmManager mAm; private final ManagedServices.UserProfiles mUserProfiles; // User id : package name : notification key : record. private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>> // User id | package name : notification key : record. private ArrayMap<String, ArrayMap<String, NotificationRecord>> mSnoozedNotifications = new ArrayMap<>(); // User id : package name : notification key : time-milliseconds . // User id | package name : notification key : time-milliseconds . // This member stores persisted snoozed notification trigger times. it persists through reboots // It should have the notifications that haven't expired or re-posted yet private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, Long>>> private final ArrayMap<String, ArrayMap<String, Long>> mPersistedSnoozedNotifications = new ArrayMap<>(); // User id : package name : notification key : creation ID . // User id | package name : notification key : creation ID . // This member stores persisted snoozed notification trigger context for the assistant // it persists through reboots. // It should have the notifications that haven't expired or re-posted yet private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, String>>> private final ArrayMap<String, ArrayMap<String, String>> mPersistedSnoozedNotificationsWithContext = new ArrayMap<>(); // notification key : package. private ArrayMap<String, String> mPackages = new ArrayMap<>(); Loading @@ -115,6 +118,10 @@ public class SnoozeHelper { mUserProfiles = userProfiles; } private String getPkgKey(@UserIdInt int userId, String pkg) { return userId + "|" + pkg; } void cleanupPersistedContext(String key){ int userId = mUsers.get(key); String pkg = mPackages.get(key); Loading Loading @@ -144,15 +151,13 @@ public class SnoozeHelper { } protected boolean isSnoozed(int userId, String pkg, String key) { return mSnoozedNotifications.containsKey(userId) && mSnoozedNotifications.get(userId).containsKey(pkg) && mSnoozedNotifications.get(userId).get(pkg).containsKey(key); return mSnoozedNotifications.containsKey(getPkgKey(userId, pkg)) && mSnoozedNotifications.get(getPkgKey(userId, pkg)).containsKey(key); } protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) { if (mSnoozedNotifications.containsKey(userId) && mSnoozedNotifications.get(userId).containsKey(pkg)) { return mSnoozedNotifications.get(userId).get(pkg).values(); if (mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))) { return mSnoozedNotifications.get(getPkgKey(userId, pkg)).values(); } return Collections.EMPTY_LIST; } Loading @@ -161,14 +166,14 @@ public class SnoozeHelper { ArrayList<NotificationRecord> getNotifications(String pkg, String groupKey, Integer userId) { ArrayList<NotificationRecord> records = new ArrayList<>(); if (mSnoozedNotifications.containsKey(userId) && mSnoozedNotifications.get(userId).containsKey(pkg)) { ArrayMap<String, NotificationRecord> packages = mSnoozedNotifications.get(userId).get(pkg); for (int i = 0; i < packages.size(); i++) { String currentGroupKey = packages.valueAt(i).getSbn().getGroup(); ArrayMap<String, NotificationRecord> allRecords = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (allRecords != null) { for (int i = 0; i < allRecords.size(); i++) { NotificationRecord r = allRecords.valueAt(i); String currentGroupKey = r.getSbn().getGroup(); if (currentGroupKey.equals(groupKey)) { records.add(packages.valueAt(i)); records.add(r); } } } Loading @@ -176,47 +181,30 @@ public class SnoozeHelper { } protected NotificationRecord getNotification(String key) { List<NotificationRecord> snoozedForUser = new ArrayList<>(); IntArray userIds = mUserProfiles.getCurrentProfileIds(); if (userIds != null) { final int userIdsSize = userIds.size(); for (int i = 0; i < userIdsSize; i++) { final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = mSnoozedNotifications.get(userIds.get(i)); if (snoozedPkgs != null) { final int snoozedPkgsSize = snoozedPkgs.size(); for (int j = 0; j < snoozedPkgsSize; j++) { final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j); if (records != null) { return records.get(key); } } } } if (!mUsers.containsKey(key) || !mPackages.containsKey(key)) { Slog.w(TAG, "Snoozed data sets no longer agree for " + key); return null; } int userId = mUsers.get(key); String pkg = mPackages.get(key); ArrayMap<String, NotificationRecord> snoozed = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (snoozed == null) { return null; } return snoozed.get(key); } protected @NonNull List<NotificationRecord> getSnoozed() { List<NotificationRecord> snoozedForUser = new ArrayList<>(); IntArray userIds = mUserProfiles.getCurrentProfileIds(); if (userIds != null) { final int N = userIds.size(); for (int i = 0; i < N; i++) { final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = mSnoozedNotifications.get(userIds.get(i)); if (snoozedPkgs != null) { final int M = snoozedPkgs.size(); for (int j = 0; j < M; j++) { final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j); if (records != null) { snoozedForUser.addAll(records.values()); // caller filters records based on the current user profiles and listener access, so just // return everything List<NotificationRecord> snoozed= new ArrayList<>(); for (String userPkgKey : mSnoozedNotifications.keySet()) { ArrayMap<String, NotificationRecord> snoozedRecords = mSnoozedNotifications.get(userPkgKey); snoozed.addAll(snoozedRecords.values()); } } } } } return snoozedForUser; return snoozed; } /** Loading Loading @@ -261,50 +249,34 @@ public class SnoozeHelper { } private <T> void storeRecord(String pkg, String key, Integer userId, ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, T object) { ArrayMap<String, ArrayMap<String, T>> targets, T object) { ArrayMap<String, ArrayMap<String, T>> records = targets.get(userId); if (records == null) { records = new ArrayMap<>(); } ArrayMap<String, T> pkgRecords = records.get(pkg); if (pkgRecords == null) { pkgRecords = new ArrayMap<>(); ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg)); if (keyToValue == null) { keyToValue = new ArrayMap<>(); } pkgRecords.put(key, object); records.put(pkg, pkgRecords); targets.put(userId, records); keyToValue.put(key, object); targets.put(getPkgKey(userId, pkg), keyToValue); } private <T> T removeRecord(String pkg, String key, Integer userId, ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets) { ArrayMap<String, ArrayMap<String, T>> targets) { T object = null; ArrayMap<String, ArrayMap<String, T>> records = targets.get(userId); if (records == null) { return null; } ArrayMap<String, T> pkgRecords = records.get(pkg); if (pkgRecords == null) { ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg)); if (keyToValue == null) { return null; } object = pkgRecords.remove(key); if (pkgRecords.size() == 0) { records.remove(pkg); } if (records.size() == 0) { targets.remove(userId); object = keyToValue.remove(key); if (keyToValue.size() == 0) { targets.remove(getPkgKey(userId, pkg)); } return object; } protected boolean cancel(int userId, String pkg, String tag, int id) { if (mSnoozedNotifications.containsKey(userId)) { ArrayMap<String, NotificationRecord> recordsForPkg = mSnoozedNotifications.get(userId).get(pkg); mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (recordsForPkg != null) { final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet(); for (Map.Entry<String, NotificationRecord> record : records) { Loading @@ -315,66 +287,50 @@ public class SnoozeHelper { } } } } return false; } protected boolean cancel(int userId, boolean includeCurrentProfiles) { int[] userIds = {userId}; if (includeCurrentProfiles) { userIds = mUserProfiles.getCurrentProfileIds().toArray(); } final int N = userIds.length; for (int i = 0; i < N; i++) { final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = mSnoozedNotifications.get(userIds[i]); if (snoozedPkgs != null) { final int M = snoozedPkgs.size(); for (int j = 0; j < M; j++) { final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j); if (records != null) { int P = records.size(); for (int k = 0; k < P; k++) { records.valueAt(k).isCanceled = true; protected void cancel(int userId, boolean includeCurrentProfiles) { if (mSnoozedNotifications.size() == 0) { return; } IntArray userIds = new IntArray(); userIds.add(userId); if (includeCurrentProfiles) { userIds = mUserProfiles.getCurrentProfileIds(); } for (ArrayMap<String, NotificationRecord> snoozedRecords : mSnoozedNotifications.values()) { for (NotificationRecord r : snoozedRecords.values()) { if (userIds.binarySearch(r.getUserId()) >= 0) { r.isCanceled = true; } return true; } } return false; } protected boolean cancel(int userId, String pkg) { if (mSnoozedNotifications.containsKey(userId)) { if (mSnoozedNotifications.get(userId).containsKey(pkg)) { ArrayMap<String, NotificationRecord> records = mSnoozedNotifications.get(userId).get(pkg); mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { return false; } int N = records.size(); for (int i = 0; i < N; i++) { records.valueAt(i).isCanceled = true; } return true; } } return false; } /** * Updates the notification record so the most up to date information is shown on re-post. */ protected void update(int userId, NotificationRecord record) { ArrayMap<String, ArrayMap<String, NotificationRecord>> records = mSnoozedNotifications.get(userId); ArrayMap<String, NotificationRecord> records = mSnoozedNotifications.get(getPkgKey(userId, record.getSbn().getPackageName())); if (records == null) { return; } ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.getSbn().getPackageName()); if (pkgRecords == null) { return; } NotificationRecord existing = pkgRecords.get(record.getKey()); pkgRecords.put(record.getKey(), record); records.put(record.getKey(), record); } protected void repost(String key) { Loading @@ -386,20 +342,18 @@ public class SnoozeHelper { protected void repost(String key, int userId) { final String pkg = mPackages.remove(key); ArrayMap<String, ArrayMap<String, NotificationRecord>> records = mSnoozedNotifications.get(userId); ArrayMap<String, NotificationRecord> records = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { return; } ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg); if (pkgRecords == null) { return; } final NotificationRecord record = pkgRecords.remove(key); final NotificationRecord record = records.remove(key); mPackages.remove(key); mUsers.remove(key); if (record != null && !record.isCanceled) { final PendingIntent pi = createPendingIntent(pkg, record.getKey(), userId); mAm.cancel(pi); MetricsLogger.action(record.getLogMaker() .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsProto.MetricsEvent.TYPE_OPEN)); Loading @@ -408,14 +362,12 @@ 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); ArrayMap<String, NotificationRecord> recordsByKey = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (recordsByKey == null) { return; } if (recordsByKey != null) { String groupSummaryKey = null; int N = recordsByKey.size(); for (int i = 0; i < N; i++) { Loading @@ -441,22 +393,15 @@ public class SnoozeHelper { } } } } } } protected void clearData(int userId, String pkg) { ArrayMap<String, ArrayMap<String, NotificationRecord>> records = mSnoozedNotifications.get(userId); ArrayMap<String, NotificationRecord> records = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { return; } ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg); if (pkgRecords == null) { return; } for (int i = pkgRecords.size() - 1; i >= 0; i--) { final NotificationRecord r = pkgRecords.removeAt(i); for (int i = records.size() - 1; i >= 0; i--) { final NotificationRecord r = records.removeAt(i); if (r != null) { mPackages.remove(r.getKey()); mUsers.remove(r.getKey()); Loading Loading @@ -495,16 +440,12 @@ public class SnoozeHelper { public void dump(PrintWriter pw, NotificationManagerService.DumpFilter filter) { pw.println("\n Snoozed notifications:"); for (int userId : mSnoozedNotifications.keySet()) { pw.print(INDENT); pw.println("user: " + userId); ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = mSnoozedNotifications.get(userId); for (String pkg : snoozedPkgs.keySet()) { pw.print(INDENT); for (String userPkgKey : mSnoozedNotifications.keySet()) { pw.print(INDENT); pw.println("package: " + pkg); Set<String> snoozedKeys = snoozedPkgs.get(pkg).keySet(); pw.println("key: " + userPkgKey); ArrayMap<String, NotificationRecord> snoozedRecords = mSnoozedNotifications.get(userPkgKey); Set<String> snoozedKeys = snoozedRecords.keySet(); for (String key : snoozedKeys) { pw.print(INDENT); pw.print(INDENT); Loading @@ -512,6 +453,24 @@ public class SnoozeHelper { pw.println(key); } } pw.println("\n Pending snoozed notifications"); for (String userPkgKey : mPersistedSnoozedNotifications.keySet()) { pw.print(INDENT); pw.println("key: " + userPkgKey); ArrayMap<String, Long> snoozedRecords = mPersistedSnoozedNotifications.get(userPkgKey); if (snoozedRecords == null) { continue; } Set<String> snoozedKeys = snoozedRecords.keySet(); for (String key : snoozedKeys) { pw.print(INDENT); pw.print(INDENT); pw.print(INDENT); pw.print(key); pw.print(INDENT); pw.println(snoozedRecords.get(key)); } } } Loading @@ -538,26 +497,18 @@ public class SnoozeHelper { void insert(T t) throws IOException; } private <T> void writeXml(XmlSerializer out, ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, String tag, ArrayMap<String, ArrayMap<String, T>> targets, String tag, Inserter<T> attributeInserter) throws IOException { synchronized (targets) { final int M = targets.size(); for (int i = 0; i < M; i++) { final ArrayMap<String, ArrayMap<String, T>> packages = targets.valueAt(i); if (packages == null) { continue; } final int N = packages.size(); for (int j = 0; j < N; j++) { final ArrayMap<String, T> keyToValue = packages.valueAt(j); if (keyToValue == null) { continue; } final int O = keyToValue.size(); for (int k = 0; k < O; k++) { T value = keyToValue.valueAt(k); String userIdPkgKey = targets.keyAt(i); // T is a String (snoozed until context) or Long (snoozed until time) ArrayMap<String, T> keyToValue = targets.valueAt(i); for (int j = 0; j < keyToValue.size(); j++) { String key = keyToValue.keyAt(j); T value = keyToValue.valueAt(j); out.startTag(null, tag); Loading @@ -565,14 +516,15 @@ public class SnoozeHelper { out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, XML_SNOOZED_NOTIFICATION_VERSION); out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, keyToValue.keyAt(k)); out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, packages.keyAt(j)); out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key); String pkg = mPackages.get(key); int userId = mUsers.get(key); out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg); out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID, targets.keyAt(i).toString()); String.valueOf(userId)); out.endTag(null, tag); } } } } Loading Loading @@ -606,7 +558,6 @@ public class SnoozeHelper { } scheduleRepost(pkg, key, userId, time - System.currentTimeMillis()); } continue; } if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) { final String creationId = parser.getAttributeValue( Loading @@ -615,18 +566,9 @@ public class SnoozeHelper { storeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext, creationId); } continue; } } catch (Exception e) { //we dont cre if it is a number format exception or a null pointer exception. //we just want to debug it and continue with our lives if (DEBUG) { Slog.d(TAG, "Exception in reading snooze data from policy xml: " + e.getMessage()); } Slog.e(TAG, "Exception in reading snooze data from policy xml", e); } } } Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +27 −0 Original line number Diff line number Diff line Loading @@ -2621,6 +2621,33 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString()); } @Test public void testSystemNotificationListenerCanUnsnooze() throws Exception { final NotificationRecord nr = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mBinderService.enqueueNotificationWithTag(PKG, PKG, "testSystemNotificationListenerCanUnsnooze", nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); waitForIdle(); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( nr.getKey(), 100, null); snoozeNotificationRunnable.run(); ManagedServices.ManagedServiceInfo listener = mListeners.new ManagedServiceInfo( null, new ComponentName(PKG, "test_class"), mUid, true, null, 0); listener.isSystem = true; when(mListeners.checkServiceTokenLocked(any())).thenReturn(listener); mBinderService.unsnoozeNotificationFromSystemListener(null, nr.getKey()); waitForIdle(); StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); assertEquals(1, notifs.length); assertNotNull(notifs[0].getKey());//mService.getNotificationRecord(nr.getSbn().getKey())); } @Test public void testSetListenerAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); Loading