summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Julia Reynolds <juliacr@google.com> 2016-11-22 09:26:46 -0500
committer Julia Reynolds <juliacr@google.com> 2016-11-28 08:53:14 -0500
commitb6c1f99bd96d2f38980f4473baf3aa908d059db2 (patch)
tree25764a650fa5b8150e401a01bedcddd312060130
parentd0835e45f7663cb6abef383b9d903429c824cd20 (diff)
Allow listeners more snoozing options.
(snooze indeterminately and unsnooze) Test: runtest systemui-notification and cts tests in same topic. Change-Id: I5ce74638f55ed796bc6b26af167b32b0040f4222
-rw-r--r--api/current.txt2
-rw-r--r--api/system-current.txt2
-rw-r--r--api/test-current.txt3
-rw-r--r--core/java/android/app/INotificationManager.aidl5
-rw-r--r--core/java/android/app/NotificationManager.java2
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java41
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java63
-rw-r--r--services/core/java/com/android/server/notification/SnoozeHelper.java36
-rw-r--r--services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java17
9 files changed, 152 insertions, 19 deletions
diff --git a/api/current.txt b/api/current.txt
index 8f7e305dfe19..32d46f40941e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35094,6 +35094,8 @@ package android.service.notification {
method public final void requestUnbind();
method public final void setNotificationsShown(java.lang.String[]);
method public final void snoozeNotification(java.lang.String, long);
+ method public final void snoozeNotification(java.lang.String);
+ method public final void unsnoozeNotification(java.lang.String);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 6a57e4888f76..a41813b6a069 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -37893,7 +37893,9 @@ package android.service.notification {
method public final void setNotificationsShown(java.lang.String[]);
method public final void setOnNotificationPostedTrim(int);
method public final void snoozeNotification(java.lang.String, long);
+ method public final void snoozeNotification(java.lang.String);
method public void unregisterAsSystemService() throws android.os.RemoteException;
+ method public final void unsnoozeNotification(java.lang.String);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
diff --git a/api/test-current.txt b/api/test-current.txt
index 004350dd2eb2..343e027b434d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5395,6 +5395,7 @@ package android.app {
method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
method public java.util.Map<java.lang.String, android.app.AutomaticZenRule> getAutomaticZenRules();
method public final int getCurrentInterruptionFilter();
+ method public android.content.ComponentName getEffectsSuppressor();
method public int getImportance();
method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
@@ -35187,6 +35188,8 @@ package android.service.notification {
method public final void requestUnbind();
method public final void setNotificationsShown(java.lang.String[]);
method public final void snoozeNotification(java.lang.String, long);
+ method public final void snoozeNotification(java.lang.String);
+ method public final void unsnoozeNotification(java.lang.String);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 15b99c6e4f7f..2a7341ae6911 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -77,7 +77,10 @@ interface INotificationManager
void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
- void snoozeNotificationFromListener(in INotificationListener token, String key, long until);
+
+ void snoozeNotificationUntilFromListener(in INotificationListener token, String key, long until);
+ void snoozeNotificationFromListener(in INotificationListener token, String key);
+ void unsnoozeNotificationFromListener(in INotificationListener token, String key);
void requestBindListener(in ComponentName component);
void requestUnbindListener(in INotificationListener token);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 2234a3850005..047f349b1e54 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -19,6 +19,7 @@ package android.app;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
+import android.annotation.TestApi;
import android.app.Notification.Builder;
import android.content.ComponentName;
import android.content.Context;
@@ -428,6 +429,7 @@ public class NotificationManager
/**
* @hide
*/
+ @TestApi
public ComponentName getEffectsSuppressor() {
INotificationManager service = getService();
try {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 45011eb49905..02ab30a1cbcd 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -534,7 +534,46 @@ public abstract class NotificationListenerService extends Service {
public final void snoozeNotification(String key, long snoozeUntil) {
if (!isBound()) return;
try {
- getNotificationInterface().snoozeNotificationFromListener(mWrapper, key, snoozeUntil);
+ getNotificationInterface().snoozeNotificationUntilFromListener(
+ mWrapper, key, snoozeUntil);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
+ * Inform the notification manager about snoozing a specific notification.
+ * <p>
+ * Use this to snooze a notification for an indeterminate time. Upon being informed, the
+ * notification manager will actually remove the notification and you will get an
+ * {@link #onNotificationRemoved(StatusBarNotification)} callback. When the
+ * snoozing period expires, you will get a
+ * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
+ * notification. Use {@link #unsnoozeNotification(String)} to restore the notification.
+ * @param key The key of the notification to snooze
+ */
+ public final void snoozeNotification(String key) {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface().snoozeNotificationFromListener(mWrapper, key);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
+ * Inform the notification manager about un-snoozing a specific notification.
+ * <p>
+ * This should only be used for notifications snoozed by this listener using
+ * {@link #snoozeNotification(String)}. Once un-snoozed, you will get a
+ * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
+ * notification.
+ * @param key The key of the notification to snooze
+ */
+ public final void unsnoozeNotification(String key) {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface().unsnoozeNotificationFromListener(mWrapper, key);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 604b3ed85e25..84c298b06284 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1837,7 +1837,7 @@ public class NotificationManagerService extends SystemService {
* @param token The binder for the listener, to check that the caller is allowed
*/
@Override
- public void snoozeNotificationFromListener(INotificationListener token, String key,
+ public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
long snoozeUntil) {
long identity = Binder.clearCallingIdentity();
try {
@@ -1849,6 +1849,38 @@ public class NotificationManagerService extends SystemService {
}
/**
+ * Allow an INotificationListener to snooze a single notification.
+ *
+ * @param token The binder for the listener, to check that the caller is allowed
+ */
+ @Override
+ public void snoozeNotificationFromListener(INotificationListener token, String key) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ snoozeNotificationInt(key, info);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Allow an INotificationListener to un-snooze a single notification.
+ *
+ * @param token The binder for the listener, to check that the caller is allowed
+ */
+ @Override
+ public void unsnoozeNotificationFromListener(INotificationListener token, String key) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ unsnoozeNotificationInt(key, info);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Allow an INotificationListener to simulate clearing (dismissing) a single notification.
*
* {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
@@ -2206,7 +2238,6 @@ public class NotificationManagerService extends SystemService {
@Override
public ComponentName getEffectsSuppressor() {
- enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
}
@@ -3686,6 +3717,34 @@ public class NotificationManagerService extends SystemService {
}
}
+ void snoozeNotificationInt(String key, ManagedServiceInfo listener) {
+ String listenerName = listener == null ? null : listener.component.toShortString();
+ // TODO: write to event log
+ if (DBG) {
+ Slog.d(TAG, String.format("snooze event(%s, %s)", key, listenerName));
+ }
+ synchronized (mNotificationList) {
+ final NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ mNotificationList.remove(r);
+ cancelNotificationLocked(r, false, REASON_SNOOZED);
+ updateLightsLocked();
+ mSnoozeHelper.snooze(r, r.getUser().getIdentifier());
+ savePolicyFile();
+ }
+ }
+ }
+
+ void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
+ String listenerName = listener == null ? null : listener.component.toShortString();
+ // TODO: write to event log
+ if (DBG) {
+ Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
+ }
+ mSnoozeHelper.repost(key, Binder.getCallingUid());
+ savePolicyFile();
+ }
+
void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
ManagedServiceInfo listener, boolean includeCurrentProfiles) {
String listenerName = listener == null ? null : listener.component.toShortString();
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 738403ed1b1e..409eef45b557 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
+import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.Log;
@@ -62,6 +63,8 @@ public class SnoozeHelper {
// User id : package name : notification key : record.
private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>>
mSnoozedNotifications = new ArrayMap<>();
+ // notification key : package.
+ private ArrayMap<String, String> mPackages = new ArrayMap<>();
private Callback mCallback;
public SnoozeHelper(Context context, Callback callback,
@@ -82,10 +85,20 @@ public class SnoozeHelper {
}
/**
- * Records a notification that should be snoozed until the given time and schedules an alarm
- * to repost at that time.
+ * Snoozes a notification and schedules an alarm to repost at that time.
*/
protected void snooze(NotificationRecord record, int userId, long until) {
+ snooze(record, userId);
+ scheduleRepost(record.sbn.getPackageName(), record.getKey(), userId, until);
+ }
+
+ /**
+ * Records a snoozed notification.
+ */
+ protected void snooze(NotificationRecord record, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "Snoozing " + record.getKey());
+ }
ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
mSnoozedNotifications.get(userId);
if (records == null) {
@@ -98,13 +111,9 @@ public class SnoozeHelper {
pkgRecords.put(record.getKey(), record);
records.put(record.sbn.getPackageName(), pkgRecords);
mSnoozedNotifications.put(userId, records);
- if (DEBUG) {
- Slog.d(TAG, "Snoozing " + record.getKey() + " until " + new Date(until));
- }
- scheduleRepost(record.sbn.getPackageName(), record.getKey(), userId, until);
+ mPackages.put(record.getKey(), record.sbn.getPackageName());
}
-
protected boolean cancel(int userId, String pkg, String tag, int id) {
if (mSnoozedNotifications.containsKey(userId)) {
ArrayMap<String, NotificationRecord> recordsForPkg =
@@ -121,6 +130,7 @@ public class SnoozeHelper {
if (key != null) {
recordsForPkg.remove(key);
cancelAlarm(userId, pkg, key);
+ mPackages.remove(key);
return true;
}
}
@@ -145,6 +155,7 @@ public class SnoozeHelper {
int P = records.size();
for (int k = 0; k < P; k++) {
cancelAlarm(userId, snoozedPkgs.keyAt(j), records.keyAt(k));
+ mPackages.remove(records.keyAt(k));
}
}
}
@@ -162,6 +173,7 @@ public class SnoozeHelper {
int N = records.size();
for (int i = 0; i < N; i++) {
cancelAlarm(userId, pkg, records.keyAt(i));
+ mPackages.remove(records.keyAt(i));
}
return true;
}
@@ -190,8 +202,8 @@ public class SnoozeHelper {
pkgRecords.put(record.getKey(), record);
}
- @VisibleForTesting
- void repost(String pkg, String key, int userId) {
+ protected void repost(String key, int userId) {
+ final String pkg = mPackages.remove(key);
ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
mSnoozedNotifications.get(userId);
if (records == null) {
@@ -202,6 +214,7 @@ public class SnoozeHelper {
return;
}
final NotificationRecord record = pkgRecords.remove(key);
+
if (record != null) {
mCallback.repost(userId, record);
}
@@ -213,7 +226,6 @@ public class SnoozeHelper {
new Intent(REPOST_ACTION)
.setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build())
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(EXTRA_PKG, pkg)
.putExtra(EXTRA_KEY, key)
.putExtra(EXTRA_USER_ID, userId),
PendingIntent.FLAG_UPDATE_CURRENT);
@@ -273,8 +285,8 @@ public class SnoozeHelper {
Slog.d(TAG, "Reposting notification");
}
if (REPOST_ACTION.equals(intent.getAction())) {
- repost(intent.getStringExtra(EXTRA_PKG), intent.getStringExtra(EXTRA_KEY),
- intent.getIntExtra(EXTRA_USER_ID, 0));
+ repost(intent.getStringExtra(EXTRA_KEY), intent.getIntExtra(EXTRA_USER_ID,
+ UserHandle.USER_SYSTEM));
}
}
};
diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
index 7a3ee7f4c068..4f6a35c8cf1d 100644
--- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
@@ -37,6 +37,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -65,7 +66,7 @@ public class SnoozeHelperTest {
}
@Test
- public void testSnooze() throws Exception {
+ public void testSnoozeForTime() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r , UserHandle.USER_SYSTEM, 1000);
verify(mAm, times(1)).setExactAndAllowWhileIdle(
@@ -75,6 +76,16 @@ public class SnoozeHelperTest {
}
@Test
+ public void testSnooze() throws Exception {
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r , UserHandle.USER_SYSTEM);
+ verify(mAm, never()).setExactAndAllowWhileIdle(
+ anyInt(), anyLong(), any(PendingIntent.class));
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ }
+
+ @Test
public void testCancelByApp() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
@@ -152,7 +163,7 @@ public class SnoozeHelperTest {
mSnoozeHelper.snooze(r , UserHandle.USER_SYSTEM, 1000);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "one", UserHandle.ALL);
mSnoozeHelper.snooze(r2 , UserHandle.USER_ALL, 1000);
- mSnoozeHelper.repost(r.sbn.getPackageName(), r.getKey(), UserHandle.USER_SYSTEM);
+ mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
}
@@ -165,7 +176,7 @@ public class SnoozeHelperTest {
mSnoozeHelper.update(UserHandle.USER_SYSTEM, r);
verify(mCallback, never()).repost(anyInt(), any(NotificationRecord.class));
- mSnoozeHelper.repost(r.sbn.getPackageName(), r.getKey(), UserHandle.USER_SYSTEM);
+ mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
}