Merge "When a profile is stopped, remove pending snoozes" into tm-qpr-dev am: bd945c1115
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19213106 Change-Id: I8f7d4a38c55bbdda380376ab02ed438e787e749e Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -1794,6 +1794,7 @@ public class NotificationManagerService extends SystemService {
|
||||
if (userHandle >= 0) {
|
||||
cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
|
||||
REASON_PROFILE_TURNED_OFF, null);
|
||||
mSnoozeHelper.clearData(userHandle);
|
||||
}
|
||||
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
|
||||
// turn off LED when user passes through lock screen
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
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;
|
||||
@@ -25,7 +24,6 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.ArrayMap;
|
||||
@@ -38,18 +36,15 @@ import android.util.TypedXmlSerializer;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import com.android.server.pm.PackageManagerService;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -59,7 +54,7 @@ import java.util.Set;
|
||||
/**
|
||||
* NotificationManagerService helper for handling snoozed notifications.
|
||||
*/
|
||||
public class SnoozeHelper {
|
||||
public final class SnoozeHelper {
|
||||
public static final int XML_SNOOZED_NOTIFICATION_VERSION = 1;
|
||||
|
||||
static final int CONCURRENT_SNOOZE_LIMIT = 500;
|
||||
@@ -68,8 +63,6 @@ public class SnoozeHelper {
|
||||
|
||||
private static final String XML_SNOOZED_NOTIFICATION = "notification";
|
||||
private static final String XML_SNOOZED_NOTIFICATION_CONTEXT = "context";
|
||||
private static final String XML_SNOOZED_NOTIFICATION_PKG = "pkg";
|
||||
private static final String XML_SNOOZED_NOTIFICATION_USER_ID = "user-id";
|
||||
private static final String XML_SNOOZED_NOTIFICATION_KEY = "key";
|
||||
//the time the snoozed notification should be reposted
|
||||
private static final String XML_SNOOZED_NOTIFICATION_TIME = "time";
|
||||
@@ -91,24 +84,19 @@ public class SnoozeHelper {
|
||||
private AlarmManager mAm;
|
||||
private final ManagedServices.UserProfiles mUserProfiles;
|
||||
|
||||
// User id | package name : notification key : record.
|
||||
private ArrayMap<String, ArrayMap<String, NotificationRecord>>
|
||||
mSnoozedNotifications = new ArrayMap<>();
|
||||
// User id | package name : notification key : time-milliseconds .
|
||||
// notification key : record.
|
||||
private ArrayMap<String, NotificationRecord> mSnoozedNotifications = new ArrayMap<>();
|
||||
// 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 final ArrayMap<String, ArrayMap<String, Long>>
|
||||
mPersistedSnoozedNotifications = new ArrayMap<>();
|
||||
// User id | package name : notification key : creation ID .
|
||||
private final ArrayMap<String, Long> mPersistedSnoozedNotifications = new ArrayMap<>();
|
||||
// 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 final ArrayMap<String, ArrayMap<String, String>>
|
||||
private final ArrayMap<String, String>
|
||||
mPersistedSnoozedNotificationsWithContext = new ArrayMap<>();
|
||||
// notification key : package.
|
||||
private ArrayMap<String, String> mPackages = new ArrayMap<>();
|
||||
// key : userId
|
||||
private ArrayMap<String, Integer> mUsers = new ArrayMap<>();
|
||||
|
||||
private Callback mCallback;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
@@ -125,21 +113,9 @@ public class SnoozeHelper {
|
||||
mUserProfiles = userProfiles;
|
||||
}
|
||||
|
||||
private String getPkgKey(@UserIdInt int userId, String pkg) {
|
||||
return userId + "|" + pkg;
|
||||
}
|
||||
|
||||
void cleanupPersistedContext(String key){
|
||||
synchronized (mLock) {
|
||||
int userId = mUsers.get(key);
|
||||
String pkg = mPackages.get(key);
|
||||
removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean canSnooze(int numberToSnooze) {
|
||||
synchronized (mLock) {
|
||||
if ((mPackages.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT) {
|
||||
if ((mSnoozedNotifications.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -150,11 +126,7 @@ public class SnoozeHelper {
|
||||
protected Long getSnoozeTimeForUnpostedNotification(int userId, String pkg, String key) {
|
||||
Long time = null;
|
||||
synchronized (mLock) {
|
||||
ArrayMap<String, Long> snoozed =
|
||||
mPersistedSnoozedNotifications.get(getPkgKey(userId, pkg));
|
||||
if (snoozed != null) {
|
||||
time = snoozed.get(key);
|
||||
}
|
||||
time = mPersistedSnoozedNotifications.get(key);
|
||||
}
|
||||
if (time == null) {
|
||||
time = 0L;
|
||||
@@ -164,29 +136,26 @@ public class SnoozeHelper {
|
||||
|
||||
protected String getSnoozeContextForUnpostedNotification(int userId, String pkg, String key) {
|
||||
synchronized (mLock) {
|
||||
ArrayMap<String, String> snoozed =
|
||||
mPersistedSnoozedNotificationsWithContext.get(getPkgKey(userId, pkg));
|
||||
if (snoozed != null) {
|
||||
return snoozed.get(key);
|
||||
}
|
||||
return mPersistedSnoozedNotificationsWithContext.get(key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean isSnoozed(int userId, String pkg, String key) {
|
||||
synchronized (mLock) {
|
||||
return mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))
|
||||
&& mSnoozedNotifications.get(getPkgKey(userId, pkg)).containsKey(key);
|
||||
return mSnoozedNotifications.containsKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) {
|
||||
synchronized (mLock) {
|
||||
if (mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))) {
|
||||
return mSnoozedNotifications.get(getPkgKey(userId, pkg)).values();
|
||||
ArrayList snoozed = new ArrayList();
|
||||
for (NotificationRecord r : mSnoozedNotifications.values()) {
|
||||
if (r.getUserId() == userId && r.getSbn().getPackageName().equals(pkg)) {
|
||||
snoozed.add(r);
|
||||
}
|
||||
}
|
||||
return snoozed;
|
||||
}
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -194,15 +163,11 @@ public class SnoozeHelper {
|
||||
String groupKey, Integer userId) {
|
||||
ArrayList<NotificationRecord> records = new ArrayList<>();
|
||||
synchronized (mLock) {
|
||||
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 (Objects.equals(currentGroupKey, groupKey)) {
|
||||
records.add(r);
|
||||
}
|
||||
for (int i = 0; i < mSnoozedNotifications.size(); i++) {
|
||||
NotificationRecord r = mSnoozedNotifications.valueAt(i);
|
||||
if (r.getSbn().getPackageName().equals(pkg) && r.getUserId() == userId
|
||||
&& Objects.equals(r.getSbn().getGroup(), groupKey)) {
|
||||
records.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,31 +176,16 @@ public class SnoozeHelper {
|
||||
|
||||
protected NotificationRecord getNotification(String key) {
|
||||
synchronized (mLock) {
|
||||
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);
|
||||
return mSnoozedNotifications.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
protected @NonNull List<NotificationRecord> getSnoozed() {
|
||||
synchronized (mLock) {
|
||||
// caller filters records based on the current user profiles and listener access, so just
|
||||
// return everything
|
||||
// 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());
|
||||
}
|
||||
snoozed.addAll(mSnoozedNotifications.values());
|
||||
return snoozed;
|
||||
}
|
||||
}
|
||||
@@ -244,15 +194,13 @@ public class SnoozeHelper {
|
||||
* Snoozes a notification and schedules an alarm to repost at that time.
|
||||
*/
|
||||
protected void snooze(NotificationRecord record, long duration) {
|
||||
String pkg = record.getSbn().getPackageName();
|
||||
String key = record.getKey();
|
||||
int userId = record.getUser().getIdentifier();
|
||||
|
||||
snooze(record);
|
||||
scheduleRepost(pkg, key, userId, duration);
|
||||
scheduleRepost(key, duration);
|
||||
Long activateAt = System.currentTimeMillis() + duration;
|
||||
synchronized (mLock) {
|
||||
storeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications, activateAt);
|
||||
mPersistedSnoozedNotifications.put(key, activateAt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,66 +208,33 @@ public class SnoozeHelper {
|
||||
* Records a snoozed notification.
|
||||
*/
|
||||
protected void snooze(NotificationRecord record, String contextId) {
|
||||
int userId = record.getUser().getIdentifier();
|
||||
if (contextId != null) {
|
||||
synchronized (mLock) {
|
||||
storeRecordLocked(record.getSbn().getPackageName(), record.getKey(),
|
||||
userId, mPersistedSnoozedNotificationsWithContext, contextId);
|
||||
mPersistedSnoozedNotificationsWithContext.put(record.getKey(), contextId);
|
||||
}
|
||||
}
|
||||
snooze(record);
|
||||
}
|
||||
|
||||
private void snooze(NotificationRecord record) {
|
||||
int userId = record.getUser().getIdentifier();
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Snoozing " + record.getKey());
|
||||
}
|
||||
synchronized (mLock) {
|
||||
storeRecordLocked(record.getSbn().getPackageName(), record.getKey(),
|
||||
userId, mSnoozedNotifications, record);
|
||||
mSnoozedNotifications.put(record.getKey(), record);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void storeRecordLocked(String pkg, String key, Integer userId,
|
||||
ArrayMap<String, ArrayMap<String, T>> targets, T object) {
|
||||
|
||||
mPackages.put(key, pkg);
|
||||
mUsers.put(key, userId);
|
||||
ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg));
|
||||
if (keyToValue == null) {
|
||||
keyToValue = new ArrayMap<>();
|
||||
}
|
||||
keyToValue.put(key, object);
|
||||
targets.put(getPkgKey(userId, pkg), keyToValue);
|
||||
}
|
||||
|
||||
private <T> T removeRecordLocked(String pkg, String key, Integer userId,
|
||||
ArrayMap<String, ArrayMap<String, T>> targets) {
|
||||
T object = null;
|
||||
ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg));
|
||||
if (keyToValue == null) {
|
||||
return null;
|
||||
}
|
||||
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) {
|
||||
synchronized (mLock) {
|
||||
ArrayMap<String, NotificationRecord> recordsForPkg =
|
||||
mSnoozedNotifications.get(getPkgKey(userId, pkg));
|
||||
if (recordsForPkg != null) {
|
||||
final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
|
||||
for (Map.Entry<String, NotificationRecord> record : records) {
|
||||
final StatusBarNotification sbn = record.getValue().getSbn();
|
||||
if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
|
||||
record.getValue().isCanceled = true;
|
||||
return true;
|
||||
}
|
||||
final Set<Map.Entry<String, NotificationRecord>> records =
|
||||
mSnoozedNotifications.entrySet();
|
||||
for (Map.Entry<String, NotificationRecord> record : records) {
|
||||
final StatusBarNotification sbn = record.getValue().getSbn();
|
||||
if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
|
||||
&& Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
|
||||
record.getValue().isCanceled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,11 +251,9 @@ public class SnoozeHelper {
|
||||
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;
|
||||
}
|
||||
for (NotificationRecord r : mSnoozedNotifications.values()) {
|
||||
if (userIds.binarySearch(r.getUserId()) >= 0) {
|
||||
r.isCanceled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,14 +261,12 @@ public class SnoozeHelper {
|
||||
|
||||
protected boolean cancel(int userId, String pkg) {
|
||||
synchronized (mLock) {
|
||||
ArrayMap<String, NotificationRecord> records =
|
||||
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;
|
||||
int n = mSnoozedNotifications.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final NotificationRecord r = mSnoozedNotifications.valueAt(i);
|
||||
if (r.getSbn().getPackageName().equals(pkg) && r.getUserId() == userId) {
|
||||
r.isCanceled = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -366,20 +277,17 @@ public class SnoozeHelper {
|
||||
*/
|
||||
protected void update(int userId, NotificationRecord record) {
|
||||
synchronized (mLock) {
|
||||
ArrayMap<String, NotificationRecord> records =
|
||||
mSnoozedNotifications.get(getPkgKey(userId, record.getSbn().getPackageName()));
|
||||
if (records == null) {
|
||||
return;
|
||||
if (mSnoozedNotifications.containsKey(record.getKey())) {
|
||||
mSnoozedNotifications.put(record.getKey(), record);
|
||||
}
|
||||
records.put(record.getKey(), record);
|
||||
}
|
||||
}
|
||||
|
||||
protected void repost(String key, boolean muteOnReturn) {
|
||||
synchronized (mLock) {
|
||||
Integer userId = mUsers.get(key);
|
||||
if (userId != null) {
|
||||
repost(key, userId, muteOnReturn);
|
||||
final NotificationRecord r = mSnoozedNotifications.get(key);
|
||||
if (r != null) {
|
||||
repost(key, r.getUserId(), muteOnReturn);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,43 +295,30 @@ public class SnoozeHelper {
|
||||
protected void repost(String key, int userId, boolean muteOnReturn) {
|
||||
NotificationRecord record;
|
||||
synchronized (mLock) {
|
||||
final String pkg = mPackages.remove(key);
|
||||
mUsers.remove(key);
|
||||
removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications);
|
||||
removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
|
||||
ArrayMap<String, NotificationRecord> records =
|
||||
mSnoozedNotifications.get(getPkgKey(userId, pkg));
|
||||
if (records == null) {
|
||||
return;
|
||||
}
|
||||
record = records.remove(key);
|
||||
|
||||
mPersistedSnoozedNotifications.remove(key);
|
||||
mPersistedSnoozedNotificationsWithContext.remove(key);
|
||||
record = mSnoozedNotifications.remove(key);
|
||||
}
|
||||
|
||||
if (record != null && !record.isCanceled) {
|
||||
final PendingIntent pi = createPendingIntent(
|
||||
record.getSbn().getPackageName(), record.getKey(), userId);
|
||||
final PendingIntent pi = createPendingIntent(record.getKey());
|
||||
mAm.cancel(pi);
|
||||
MetricsLogger.action(record.getLogMaker()
|
||||
.setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
|
||||
.setType(MetricsProto.MetricsEvent.TYPE_OPEN));
|
||||
mCallback.repost(userId, record, muteOnReturn);
|
||||
mCallback.repost(record.getUserId(), record, muteOnReturn);
|
||||
}
|
||||
}
|
||||
|
||||
protected void repostGroupSummary(String pkg, int userId, String groupKey) {
|
||||
synchronized (mLock) {
|
||||
ArrayMap<String, NotificationRecord> recordsByKey
|
||||
= mSnoozedNotifications.get(getPkgKey(userId, pkg));
|
||||
if (recordsByKey == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String groupSummaryKey = null;
|
||||
int N = recordsByKey.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i);
|
||||
if (potentialGroupSummary.getSbn().isGroup()
|
||||
int n = mSnoozedNotifications.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final NotificationRecord potentialGroupSummary = mSnoozedNotifications.valueAt(i);
|
||||
if (potentialGroupSummary.getSbn().getPackageName().equals(pkg)
|
||||
&& potentialGroupSummary.getUserId() == userId
|
||||
&& potentialGroupSummary.getSbn().isGroup()
|
||||
&& potentialGroupSummary.getNotification().isGroupSummary()
|
||||
&& groupKey.equals(potentialGroupSummary.getGroupKey())) {
|
||||
groupSummaryKey = potentialGroupSummary.getKey();
|
||||
@@ -432,16 +327,14 @@ public class SnoozeHelper {
|
||||
}
|
||||
|
||||
if (groupSummaryKey != null) {
|
||||
NotificationRecord record = recordsByKey.remove(groupSummaryKey);
|
||||
mPackages.remove(groupSummaryKey);
|
||||
mUsers.remove(groupSummaryKey);
|
||||
NotificationRecord record = mSnoozedNotifications.remove(groupSummaryKey);
|
||||
|
||||
if (record != null && !record.isCanceled) {
|
||||
Runnable runnable = () -> {
|
||||
MetricsLogger.action(record.getLogMaker()
|
||||
.setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
|
||||
.setType(MetricsProto.MetricsEvent.TYPE_OPEN));
|
||||
mCallback.repost(userId, record, false);
|
||||
mCallback.repost(record.getUserId(), record, false);
|
||||
};
|
||||
runnable.run();
|
||||
}
|
||||
@@ -451,20 +344,17 @@ public class SnoozeHelper {
|
||||
|
||||
protected void clearData(int userId, String pkg) {
|
||||
synchronized (mLock) {
|
||||
ArrayMap<String, NotificationRecord> records =
|
||||
mSnoozedNotifications.get(getPkgKey(userId, pkg));
|
||||
if (records == null) {
|
||||
return;
|
||||
}
|
||||
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());
|
||||
int n = mSnoozedNotifications.size();
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
final NotificationRecord record = mSnoozedNotifications.valueAt(i);
|
||||
if (record.getUserId() == userId && record.getSbn().getPackageName().equals(pkg)) {
|
||||
mSnoozedNotifications.removeAt(i);
|
||||
mPersistedSnoozedNotificationsWithContext.remove(record.getKey());
|
||||
mPersistedSnoozedNotifications.remove(record.getKey());
|
||||
Runnable runnable = () -> {
|
||||
final PendingIntent pi = createPendingIntent(pkg, r.getKey(), userId);
|
||||
final PendingIntent pi = createPendingIntent(record.getKey());
|
||||
mAm.cancel(pi);
|
||||
MetricsLogger.action(r.getLogMaker()
|
||||
MetricsLogger.action(record.getLogMaker()
|
||||
.setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
|
||||
.setType(MetricsProto.MetricsEvent.TYPE_DISMISS));
|
||||
};
|
||||
@@ -474,47 +364,61 @@ public class SnoozeHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private PendingIntent createPendingIntent(String pkg, String key, int userId) {
|
||||
protected void clearData(int userId) {
|
||||
synchronized (mLock) {
|
||||
int n = mSnoozedNotifications.size();
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
final NotificationRecord record = mSnoozedNotifications.valueAt(i);
|
||||
if (record.getUserId() == userId) {
|
||||
mSnoozedNotifications.removeAt(i);
|
||||
mPersistedSnoozedNotificationsWithContext.remove(record.getKey());
|
||||
mPersistedSnoozedNotifications.remove(record.getKey());
|
||||
|
||||
Runnable runnable = () -> {
|
||||
final PendingIntent pi = createPendingIntent(record.getKey());
|
||||
mAm.cancel(pi);
|
||||
MetricsLogger.action(record.getLogMaker()
|
||||
.setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
|
||||
.setType(MetricsProto.MetricsEvent.TYPE_DISMISS));
|
||||
};
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PendingIntent createPendingIntent(String key) {
|
||||
return PendingIntent.getBroadcast(mContext,
|
||||
REQUEST_CODE_REPOST,
|
||||
new Intent(REPOST_ACTION)
|
||||
.setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
|
||||
.setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build())
|
||||
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
|
||||
.putExtra(EXTRA_KEY, key)
|
||||
.putExtra(EXTRA_USER_ID, userId),
|
||||
.putExtra(EXTRA_KEY, key),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
}
|
||||
|
||||
public void scheduleRepostsForPersistedNotifications(long currentTime) {
|
||||
synchronized (mLock) {
|
||||
for (ArrayMap<String, Long> snoozed : mPersistedSnoozedNotifications.values()) {
|
||||
for (int i = 0; i < snoozed.size(); i++) {
|
||||
String key = snoozed.keyAt(i);
|
||||
Long time = snoozed.valueAt(i);
|
||||
String pkg = mPackages.get(key);
|
||||
Integer userId = mUsers.get(key);
|
||||
if (time == null || pkg == null || userId == null) {
|
||||
Slog.w(TAG, "data out of sync: " + time + "|" + pkg + "|" + userId);
|
||||
continue;
|
||||
}
|
||||
if (time != null && time > currentTime) {
|
||||
scheduleRepostAtTime(pkg, key, userId, time);
|
||||
}
|
||||
for (int i = 0; i < mPersistedSnoozedNotifications.size(); i++) {
|
||||
String key = mPersistedSnoozedNotifications.keyAt(i);
|
||||
Long time = mPersistedSnoozedNotifications.valueAt(i);
|
||||
if (time != null && time > currentTime) {
|
||||
scheduleRepostAtTime(key, time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleRepost(String pkg, String key, int userId, long duration) {
|
||||
scheduleRepostAtTime(pkg, key, userId, System.currentTimeMillis() + duration);
|
||||
private void scheduleRepost(String key, long duration) {
|
||||
scheduleRepostAtTime(key, System.currentTimeMillis() + duration);
|
||||
}
|
||||
|
||||
private void scheduleRepostAtTime(String pkg, String key, int userId, long time) {
|
||||
private void scheduleRepostAtTime(String key, long time) {
|
||||
Runnable runnable = () -> {
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
final PendingIntent pi = createPendingIntent(pkg, key, userId);
|
||||
final PendingIntent pi = createPendingIntent(key);
|
||||
mAm.cancel(pi);
|
||||
if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + new Date(time));
|
||||
mAm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pi);
|
||||
@@ -528,37 +432,14 @@ public class SnoozeHelper {
|
||||
public void dump(PrintWriter pw, NotificationManagerService.DumpFilter filter) {
|
||||
synchronized (mLock) {
|
||||
pw.println("\n Snoozed notifications:");
|
||||
for (String userPkgKey : mSnoozedNotifications.keySet()) {
|
||||
for (String key : mSnoozedNotifications.keySet()) {
|
||||
pw.print(INDENT);
|
||||
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);
|
||||
pw.print(INDENT);
|
||||
pw.println(key);
|
||||
}
|
||||
pw.println("key: " + key);
|
||||
}
|
||||
pw.println("\n Pending snoozed notifications");
|
||||
for (String userPkgKey : mPersistedSnoozedNotifications.keySet()) {
|
||||
for (String key : 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));
|
||||
}
|
||||
pw.println("key: " + key + " until: " + mPersistedSnoozedNotifications.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -589,37 +470,22 @@ public class SnoozeHelper {
|
||||
void insert(T t) throws IOException;
|
||||
}
|
||||
|
||||
private <T> void writeXml(TypedXmlSerializer out,
|
||||
ArrayMap<String, ArrayMap<String, T>> targets, String tag,
|
||||
Inserter<T> attributeInserter)
|
||||
throws IOException {
|
||||
final int M = targets.size();
|
||||
for (int i = 0; i < M; i++) {
|
||||
private <T> void writeXml(TypedXmlSerializer out, ArrayMap<String, T> targets, String tag,
|
||||
Inserter<T> attributeInserter) throws IOException {
|
||||
for (int j = 0; j < targets.size(); j++) {
|
||||
String key = targets.keyAt(j);
|
||||
// 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);
|
||||
String pkg = mPackages.get(key);
|
||||
Integer userId = mUsers.get(key);
|
||||
T value = targets.valueAt(j);
|
||||
|
||||
if (pkg == null || userId == null) {
|
||||
Slog.w(TAG, "pkg " + pkg + " or user " + userId + " missing for " + key);
|
||||
continue;
|
||||
}
|
||||
out.startTag(null, tag);
|
||||
|
||||
out.startTag(null, tag);
|
||||
attributeInserter.insert(value);
|
||||
|
||||
attributeInserter.insert(value);
|
||||
out.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
|
||||
XML_SNOOZED_NOTIFICATION_VERSION);
|
||||
out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key);
|
||||
|
||||
out.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
|
||||
XML_SNOOZED_NOTIFICATION_VERSION);
|
||||
out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key);
|
||||
out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg);
|
||||
out.attributeInt(null, XML_SNOOZED_NOTIFICATION_USER_ID, userId);
|
||||
|
||||
out.endTag(null, tag);
|
||||
}
|
||||
out.endTag(null, tag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,16 +505,12 @@ public class SnoozeHelper {
|
||||
== XML_SNOOZED_NOTIFICATION_VERSION) {
|
||||
try {
|
||||
final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY);
|
||||
final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG);
|
||||
final int userId = parser.getAttributeInt(
|
||||
null, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL);
|
||||
if (tag.equals(XML_SNOOZED_NOTIFICATION)) {
|
||||
final Long time = parser.getAttributeLong(
|
||||
null, XML_SNOOZED_NOTIFICATION_TIME, 0);
|
||||
if (time > currentTime) { //only read new stuff
|
||||
synchronized (mLock) {
|
||||
storeRecordLocked(
|
||||
pkg, key, userId, mPersistedSnoozedNotifications, time);
|
||||
mPersistedSnoozedNotifications.put(key, time);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -656,9 +518,7 @@ public class SnoozeHelper {
|
||||
final String creationId = parser.getAttributeValue(
|
||||
null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID);
|
||||
synchronized (mLock) {
|
||||
storeRecordLocked(
|
||||
pkg, key, userId, mPersistedSnoozedNotificationsWithContext,
|
||||
creationId);
|
||||
mPersistedSnoozedNotificationsWithContext.put(key, creationId);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.android.server.notification.SnoozeHelper.EXTRA_KEY;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
@@ -39,7 +40,6 @@ import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
@@ -50,7 +50,6 @@ import android.util.Xml;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.util.FastXmlSerializer;
|
||||
import com.android.server.UiServiceTestCase;
|
||||
import com.android.server.pm.PackageManagerService;
|
||||
|
||||
@@ -60,9 +59,7 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
@@ -197,18 +194,6 @@ public class SnoozeHelperTest extends UiServiceTestCase {
|
||||
systemUser, "pkg", r.getKey()).longValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCleanupContextShouldRemovePersistedRecord() {
|
||||
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
|
||||
mSnoozeHelper.snooze(r, "context");
|
||||
mSnoozeHelper.cleanupPersistedContext(r.getSbn().getKey());
|
||||
assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
|
||||
r.getUser().getIdentifier(),
|
||||
r.getSbn().getPackageName(),
|
||||
r.getSbn().getKey()
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadNoneSnoozedNotification() throws XmlPullParserException,
|
||||
IOException, InterruptedException {
|
||||
@@ -219,8 +204,9 @@ public class SnoozeHelperTest extends UiServiceTestCase {
|
||||
assertEquals("should see a zero value for unsnoozed notification",
|
||||
0L,
|
||||
mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
|
||||
UserHandle.SYSTEM.getIdentifier(),
|
||||
"not_my_package", r.getKey()).longValue());
|
||||
UserHandle.SYSTEM.getIdentifier(), "not_my_package",
|
||||
getNotificationRecord("not_my_package", 1, "one",
|
||||
UserHandle.SYSTEM).getKey()).longValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -592,7 +578,7 @@ public class SnoozeHelperTest extends UiServiceTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearData() {
|
||||
public void testClearData_userPackage() {
|
||||
// snooze 2 from same package
|
||||
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
|
||||
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
|
||||
@@ -615,17 +601,72 @@ public class SnoozeHelperTest extends UiServiceTestCase {
|
||||
verify(mAm, times(4)).cancel(any(PendingIntent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearData_user() {
|
||||
// snooze 2 from same package
|
||||
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
|
||||
NotificationRecord r2 = getNotificationRecord("pkg2", 2, "two", UserHandle.SYSTEM);
|
||||
NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
|
||||
NotificationRecord r4 = getNotificationRecord("pkg", 2, "two", UserHandle.ALL);
|
||||
mSnoozeHelper.snooze(r, 1000);
|
||||
mSnoozeHelper.snooze(r2, 1000);
|
||||
mSnoozeHelper.snooze(r3, "until");
|
||||
mSnoozeHelper.snooze(r4, "until");
|
||||
|
||||
assertTrue(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
|
||||
assertTrue(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
|
||||
assertTrue(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
|
||||
assertTrue(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_ALL, r4.getSbn().getPackageName(), r4.getKey()));
|
||||
|
||||
// clear data
|
||||
mSnoozeHelper.clearData(UserHandle.USER_SYSTEM);
|
||||
|
||||
// nothing in USER_SYSTEM snoozed; alarms canceled
|
||||
assertFalse(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
|
||||
assertFalse(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
|
||||
assertFalse(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
|
||||
assertTrue(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, r4.getSbn().getPackageName(), r4.getKey()));
|
||||
|
||||
assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
|
||||
r3.getUser().getIdentifier(), r3.getSbn().getPackageName(),
|
||||
r3.getSbn().getKey()));
|
||||
assertNotNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
|
||||
r4.getUser().getIdentifier(), r4.getSbn().getPackageName(),
|
||||
r4.getSbn().getKey()));
|
||||
assertEquals(0L, mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
|
||||
r.getUser().getIdentifier(), r.getSbn().getPackageName(),
|
||||
r.getSbn().getKey()).longValue());
|
||||
assertEquals(0L, mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
|
||||
r2.getUser().getIdentifier(), r2.getSbn().getPackageName(),
|
||||
r2.getSbn().getKey()).longValue());
|
||||
|
||||
// 2 for initial timed-snoozes, once each for canceling the USER_SYSTEM snoozes
|
||||
verify(mAm, times(5)).cancel(any(PendingIntent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearData_otherRecordsUntouched() {
|
||||
// 2 packages, 2 users
|
||||
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
|
||||
NotificationRecord rb = getNotificationRecord("pkg", 1, "oneb", UserHandle.SYSTEM);
|
||||
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.ALL);
|
||||
NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
|
||||
mSnoozeHelper.snooze(r, 1000);
|
||||
mSnoozeHelper.snooze(rb, "until");
|
||||
mSnoozeHelper.snooze(r2, 1000);
|
||||
mSnoozeHelper.snooze(r3, 1000);
|
||||
assertTrue(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
|
||||
assertTrue(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, rb.getSbn().getPackageName(), rb.getKey()));
|
||||
assertTrue(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_ALL, r2.getSbn().getPackageName(), r2.getKey()));
|
||||
assertTrue(mSnoozeHelper.isSnoozed(
|
||||
@@ -636,12 +677,22 @@ public class SnoozeHelperTest extends UiServiceTestCase {
|
||||
|
||||
assertFalse(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
|
||||
assertFalse(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, rb.getSbn().getPackageName(), rb.getKey()));
|
||||
assertTrue(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_ALL, r2.getSbn().getPackageName(), r2.getKey()));
|
||||
assertTrue(mSnoozeHelper.isSnoozed(
|
||||
UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
|
||||
|
||||
assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
|
||||
rb.getUser().getIdentifier(), rb.getSbn().getPackageName(),
|
||||
rb.getSbn().getKey()));
|
||||
assertEquals(0L, mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
|
||||
r.getUser().getIdentifier(), r.getSbn().getPackageName(),
|
||||
r.getSbn().getKey()).longValue());
|
||||
|
||||
// once for each initial snooze, once for canceling one snooze
|
||||
verify(mAm, times(4)).cancel(any(PendingIntent.class));
|
||||
verify(mAm, times(5)).cancel(any(PendingIntent.class));
|
||||
}
|
||||
|
||||
private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
|
||||
|
||||
Reference in New Issue
Block a user