• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.service.notification;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SdkConstant;
22 import android.annotation.SystemApi;
23 import android.annotation.TestApi;
24 import android.app.NotificationChannel;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.Looper;
30 import android.os.Message;
31 import android.os.RemoteException;
32 import android.util.Log;
33 import com.android.internal.os.SomeArgs;
34 
35 import java.util.ArrayList;
36 import java.util.List;
37 
38 /**
39  * A service that helps the user manage notifications.
40  * @hide
41  */
42 @SystemApi
43 @TestApi
44 public abstract class NotificationAssistantService extends NotificationListenerService {
45     private static final String TAG = "NotificationAssistants";
46 
47     /**
48      * The {@link Intent} that must be declared as handled by the service.
49      */
50     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
51     public static final String SERVICE_INTERFACE
52             = "android.service.notification.NotificationAssistantService";
53 
54     private Handler mHandler;
55 
56     @Override
attachBaseContext(Context base)57     protected void attachBaseContext(Context base) {
58         super.attachBaseContext(base);
59         mHandler = new MyHandler(getContext().getMainLooper());
60     }
61 
62     @Override
onBind(Intent intent)63     public final IBinder onBind(Intent intent) {
64         if (mWrapper == null) {
65             mWrapper = new NotificationAssistantServiceWrapper();
66         }
67         return mWrapper;
68     }
69 
70     /**
71      * A notification was snoozed until a context. For use with
72      * {@link Adjustment#KEY_SNOOZE_CRITERIA}. When the device reaches the given context, the
73      * assistant should restore the notification with {@link #unsnoozeNotification(String)}.
74      *
75      * @param sbn the notification to snooze
76      * @param snoozeCriterionId the {@link SnoozeCriterion#getId()} representing a device context.
77      */
onNotificationSnoozedUntilContext(StatusBarNotification sbn, String snoozeCriterionId)78     abstract public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
79             String snoozeCriterionId);
80 
81     /**
82      * A notification was posted by an app. Called before alert.
83      *
84      * @param sbn the new notification
85      * @return an adjustment or null to take no action, within 100ms.
86      */
onNotificationEnqueued(StatusBarNotification sbn)87     abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn);
88 
89     /**
90      * Updates a notification.  N.B. this won’t cause
91      * an existing notification to alert, but might allow a future update to
92      * this notification to alert.
93      *
94      * @param adjustment the adjustment with an explanation
95      */
adjustNotification(Adjustment adjustment)96     public final void adjustNotification(Adjustment adjustment) {
97         if (!isBound()) return;
98         try {
99             getNotificationInterface().applyAdjustmentFromAssistant(mWrapper, adjustment);
100         } catch (android.os.RemoteException ex) {
101             Log.v(TAG, "Unable to contact notification manager", ex);
102             throw ex.rethrowFromSystemServer();
103         }
104     }
105 
106     /**
107      * Updates existing notifications. Re-ranking won't occur until all adjustments are applied.
108      * N.B. this won’t cause an existing notification to alert, but might allow a future update to
109      * these notifications to alert.
110      *
111      * @param adjustments a list of adjustments with explanations
112      */
adjustNotifications(List<Adjustment> adjustments)113     public final void adjustNotifications(List<Adjustment> adjustments) {
114         if (!isBound()) return;
115         try {
116             getNotificationInterface().applyAdjustmentsFromAssistant(mWrapper, adjustments);
117         } catch (android.os.RemoteException ex) {
118             Log.v(TAG, "Unable to contact notification manager", ex);
119             throw ex.rethrowFromSystemServer();
120         }
121     }
122 
123     /**
124      * Inform the notification manager about un-snoozing a specific notification.
125      * <p>
126      * This should only be used for notifications snoozed by this listener using
127      * {@link #snoozeNotification(String, String)}. Once un-snoozed, you will get a
128      * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
129      * notification.
130      * @param key The key of the notification to snooze
131      */
unsnoozeNotification(String key)132     public final void unsnoozeNotification(String key) {
133         if (!isBound()) return;
134         try {
135             getNotificationInterface().unsnoozeNotificationFromAssistant(mWrapper, key);
136         } catch (android.os.RemoteException ex) {
137             Log.v(TAG, "Unable to contact notification manager", ex);
138         }
139     }
140 
141     private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper {
142         @Override
onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder)143         public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder) {
144             StatusBarNotification sbn;
145             try {
146                 sbn = sbnHolder.get();
147             } catch (RemoteException e) {
148                 Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e);
149                 return;
150             }
151 
152             SomeArgs args = SomeArgs.obtain();
153             args.arg1 = sbn;
154             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED,
155                     args).sendToTarget();
156         }
157 
158         @Override
onNotificationSnoozedUntilContext( IStatusBarNotificationHolder sbnHolder, String snoozeCriterionId)159         public void onNotificationSnoozedUntilContext(
160                 IStatusBarNotificationHolder sbnHolder, String snoozeCriterionId)
161                 throws RemoteException {
162             StatusBarNotification sbn;
163             try {
164                 sbn = sbnHolder.get();
165             } catch (RemoteException e) {
166                 Log.w(TAG, "onNotificationSnoozed: Error receiving StatusBarNotification", e);
167                 return;
168             }
169 
170             SomeArgs args = SomeArgs.obtain();
171             args.arg1 = sbn;
172             args.arg2 = snoozeCriterionId;
173             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_SNOOZED,
174                     args).sendToTarget();
175         }
176     }
177 
178     private final class MyHandler extends Handler {
179         public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
180         public static final int MSG_ON_NOTIFICATION_SNOOZED = 2;
181 
MyHandler(Looper looper)182         public MyHandler(Looper looper) {
183             super(looper, null, false);
184         }
185 
186         @Override
handleMessage(Message msg)187         public void handleMessage(Message msg) {
188             switch (msg.what) {
189                 case MSG_ON_NOTIFICATION_ENQUEUED: {
190                     SomeArgs args = (SomeArgs) msg.obj;
191                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
192                     args.recycle();
193                     Adjustment adjustment = onNotificationEnqueued(sbn);
194                     if (adjustment != null) {
195                         if (!isBound()) return;
196                         try {
197                             getNotificationInterface().applyEnqueuedAdjustmentFromAssistant(
198                                     mWrapper, adjustment);
199                         } catch (android.os.RemoteException ex) {
200                             Log.v(TAG, "Unable to contact notification manager", ex);
201                             throw ex.rethrowFromSystemServer();
202                         }
203                     }
204                     break;
205                 }
206                 case MSG_ON_NOTIFICATION_SNOOZED: {
207                     SomeArgs args = (SomeArgs) msg.obj;
208                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
209                     String snoozeCriterionId = (String) args.arg2;
210                     args.recycle();
211                     onNotificationSnoozedUntilContext(sbn, snoozeCriterionId);
212                     break;
213                 }
214             }
215         }
216     }
217 }
218