diff options
| author | 2016-11-22 09:26:46 -0500 | |
|---|---|---|
| committer | 2016-11-28 08:53:14 -0500 | |
| commit | b6c1f99bd96d2f38980f4473baf3aa908d059db2 (patch) | |
| tree | 25764a650fa5b8150e401a01bedcddd312060130 | |
| parent | d0835e45f7663cb6abef383b9d903429c824cd20 (diff) | |
Allow listeners more snoozing options.
(snooze indeterminately and unsnooze)
Test: runtest systemui-notification and cts tests in same topic.
Change-Id: I5ce74638f55ed796bc6b26af167b32b0040f4222
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); } |