Add support for snoozing notifications

To a notification listener, snoozing will appear as a cancel
(with reason snoozed) followed by a post (when the snooze period
ends).

Apps can repost a snoozed notification, but the updates will not be shown
to the user until the snooze period ends.

Snoozing is canceled if the posting app or a notification listener
cancels the notification.

Any notification listener can snooze a notification. Technically apps
can snooze their own notifications also, though that's not public.

In this iteration snoozed notifications will be lost on device reboot.

Test: included. Also, various post, snooze, update, cancel tests with
a listener.

Bug: 30997603
Change-Id: I568a6448196f0992a17d20a4dac296321ec5092b
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 28224e8..c87eef9 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -78,6 +78,8 @@
     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 requestBindListener(in ComponentName component);
     void requestUnbindListener(in INotificationListener token);
     void requestBindProvider(in ComponentName component);
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index b13e162..e6f58f5 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -450,6 +450,28 @@
     }
 
     /**
+     * Inform the notification manager about snoozing a specific notification.
+     * <p>
+     * Use this if your listener has a user interface that allows the user to snooze a notification
+     * until a given time.  It should be called after the user snoozes a single notification using
+     * your UI; 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.
+     * @param key The key of the notification to snooze
+     * @param snoozeUntil A time in the future, in milliseconds.
+     */
+    public final void snoozeNotification(String key, long snoozeUntil) {
+        if (!isBound()) return;
+        try {
+            getNotificationInterface().snoozeNotificationFromListener(mWrapper, key, snoozeUntil);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
+    }
+
+    /**
      * Inform the notification manager that these notifications have been viewed by the
      * user. This should only be called when there is sufficient confidence that the user is
      * looking at the notifications, such as when the notifications appear on the screen due to
diff --git a/core/java/android/service/notification/NotificationRankerService.java b/core/java/android/service/notification/NotificationRankerService.java
index 261d82d..928d5d8 100644
--- a/core/java/android/service/notification/NotificationRankerService.java
+++ b/core/java/android/service/notification/NotificationRankerService.java
@@ -102,6 +102,9 @@
     /** Notification was canceled by the user banning the channel. */
     public static final int REASON_CHANNEL_BANNED = 17;
 
+    /** Notification was snoozed. */
+    public static final int REASON_SNOOZED = 18;
+
     private Handler mHandler;
 
     /** @hide */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0942a24..9d551a4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -436,6 +436,7 @@
 
     <protected-broadcast android:name="ScheduleConditionProvider.EVALUATE" />
     <protected-broadcast android:name="EventConditionProvider.EVALUATE" />
+    <protected-broadcast android:name="SnoozeHelper.EVALUATE" />
     <protected-broadcast android:name="wifi_scan_available" />
 
     <protected-broadcast android:name="action.cne.started" />