• 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.SdkConstant;
20 import android.annotation.SystemApi;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.net.Uri;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.Parcel;
31 import android.os.Parcelable;
32 import android.os.RemoteException;
33 import android.util.Log;
34 import com.android.internal.os.SomeArgs;
35 
36 import java.util.List;
37 
38 /**
39  * A service that helps the user manage notifications. This class is only used to
40  * extend the framework service and may not be implemented by non-framework components.
41  * @hide
42  */
43 @SystemApi
44 public abstract class NotificationRankerService extends NotificationListenerService {
45     private static final String TAG = "NotificationRankers";
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.NotificationRankerService";
53 
54     /** Notification was canceled by the status bar reporting a click. */
55     public static final int REASON_DELEGATE_CLICK = 1;
56 
57     /** Notification was canceled by the status bar reporting a user dismissal. */
58     public static final int REASON_DELEGATE_CANCEL = 2;
59 
60     /** Notification was canceled by the status bar reporting a user dismiss all. */
61     public static final int REASON_DELEGATE_CANCEL_ALL = 3;
62 
63     /** Notification was canceled by the status bar reporting an inflation error. */
64     public static final int REASON_DELEGATE_ERROR = 4;
65 
66     /** Notification was canceled by the package manager modifying the package. */
67     public static final int REASON_PACKAGE_CHANGED = 5;
68 
69     /** Notification was canceled by the owning user context being stopped. */
70     public static final int REASON_USER_STOPPED = 6;
71 
72     /** Notification was canceled by the user banning the package. */
73     public static final int REASON_PACKAGE_BANNED = 7;
74 
75     /** Notification was canceled by the app canceling this specific notification. */
76     public static final int REASON_APP_CANCEL = 8;
77 
78     /** Notification was canceled by the app cancelling all its notifications. */
79     public static final int REASON_APP_CANCEL_ALL = 9;
80 
81     /** Notification was canceled by a listener reporting a user dismissal. */
82     public static final int REASON_LISTENER_CANCEL = 10;
83 
84     /** Notification was canceled by a listener reporting a user dismiss all. */
85     public static final int REASON_LISTENER_CANCEL_ALL = 11;
86 
87     /** Notification was canceled because it was a member of a canceled group. */
88     public static final int REASON_GROUP_SUMMARY_CANCELED = 12;
89 
90     /** Notification was canceled because it was an invisible member of a group. */
91     public static final int REASON_GROUP_OPTIMIZATION = 13;
92 
93     /** Notification was canceled by the device administrator suspending the package. */
94     public static final int REASON_PACKAGE_SUSPENDED = 14;
95 
96     /** Notification was canceled by the owning managed profile being turned off. */
97     public static final int REASON_PROFILE_TURNED_OFF = 15;
98 
99     /** Autobundled summary notification was canceled because its group was unbundled */
100     public static final int REASON_UNAUTOBUNDLED = 16;
101 
102     private Handler mHandler;
103 
104     /** @hide */
105     @Override
registerAsSystemService(Context context, ComponentName componentName, int currentUser)106     public void registerAsSystemService(Context context, ComponentName componentName,
107             int currentUser)  {
108         throw new UnsupportedOperationException("the ranker lifecycle is managed by the system.");
109     }
110 
111     /** @hide */
112     @Override
unregisterAsSystemService()113     public void unregisterAsSystemService()  {
114         throw new UnsupportedOperationException("the ranker lifecycle is managed by the system.");
115     }
116 
117     @Override
attachBaseContext(Context base)118     protected void attachBaseContext(Context base) {
119         super.attachBaseContext(base);
120         mHandler = new MyHandler(getContext().getMainLooper());
121     }
122 
123     @Override
onBind(Intent intent)124     public final IBinder onBind(Intent intent) {
125         if (mWrapper == null) {
126             mWrapper = new NotificationRankingServiceWrapper();
127         }
128         return mWrapper;
129     }
130 
131     /**
132      * A notification was posted by an app. Called before alert.
133      *
134      * @param sbn the new notification
135      * @param importance the initial importance of the notification.
136      * @param user true if the initial importance reflects an explicit user preference.
137      * @return an adjustment or null to take no action, within 100ms.
138      */
onNotificationEnqueued(StatusBarNotification sbn, int importance, boolean user)139     abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
140           int importance, boolean user);
141 
142     /**
143      * The visibility of a notification has changed.
144      *
145      * @param key the notification key
146      * @param time milliseconds since midnight, January 1, 1970 UTC.
147      * @param visible true if the notification became visible, false if hidden.
148      */
onNotificationVisibilityChanged(String key, long time, boolean visible)149     public void onNotificationVisibilityChanged(String key, long time, boolean visible)
150     {
151         // Do nothing, Override this to collect visibility statistics.
152     }
153 
154     /**
155      * The user clicked on a notification.
156      *
157      * @param key the notification key
158      * @param time milliseconds since midnight, January 1, 1970 UTC.
159      */
onNotificationClick(String key, long time)160     public void onNotificationClick(String key, long time)
161     {
162         // Do nothing, Override this to collect click statistics
163     }
164 
165     /**
166      * The user clicked on a notification action.
167      *
168      * @param key the notification key
169      * @param time milliseconds since midnight, January 1, 1970 UTC.
170      * @param actionIndex the index of the action button that was pressed.
171      */
onNotificationActionClick(String key, long time, int actionIndex)172     public void onNotificationActionClick(String key, long time, int actionIndex)
173     {
174         // Do nothing, Override this to collect action button click statistics
175     }
176 
177     /**
178      * A notification was removed.
179 
180      * @param key the notification key
181      * @param time milliseconds since midnight, January 1, 1970 UTC.
182      * @param reason see {@link #REASON_LISTENER_CANCEL}, etc.
183      */
onNotificationRemoved(String key, long time, int reason)184     public void onNotificationRemoved(String key, long time, int reason) {
185         // Do nothing, Override this to collect dismissal statistics
186     }
187 
188     /**
189      * Updates a notification.  N.B. this won’t cause
190      * an existing notification to alert, but might allow a future update to
191      * this notification to alert.
192      *
193      * @param adjustment the adjustment with an explanation
194      */
adjustNotification(Adjustment adjustment)195     public final void adjustNotification(Adjustment adjustment) {
196         if (!isBound()) return;
197         try {
198             getNotificationInterface().applyAdjustmentFromRankerService(mWrapper, adjustment);
199         } catch (android.os.RemoteException ex) {
200             Log.v(TAG, "Unable to contact notification manager", ex);
201         }
202     }
203 
204     /**
205      * Updates existing notifications. Re-ranking won't occur until all adjustments are applied.
206      * N.B. this won’t cause an existing notification to alert, but might allow a future update to
207      * these notifications to alert.
208      *
209      * @param adjustments a list of adjustments with explanations
210      */
adjustNotifications(List<Adjustment> adjustments)211     public final void adjustNotifications(List<Adjustment> adjustments) {
212         if (!isBound()) return;
213         try {
214             getNotificationInterface().applyAdjustmentsFromRankerService(mWrapper, adjustments);
215         } catch (android.os.RemoteException ex) {
216             Log.v(TAG, "Unable to contact notification manager", ex);
217         }
218     }
219 
220     private class NotificationRankingServiceWrapper extends NotificationListenerWrapper {
221         @Override
onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder, int importance, boolean user)222         public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
223                 int importance, boolean user) {
224             StatusBarNotification sbn;
225             try {
226                 sbn = sbnHolder.get();
227             } catch (RemoteException e) {
228                 Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e);
229                 return;
230             }
231 
232             SomeArgs args = SomeArgs.obtain();
233             args.arg1 = sbn;
234             args.argi1 = importance;
235             args.argi2 = user ? 1 : 0;
236             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED,
237                     args).sendToTarget();
238         }
239 
240         @Override
onNotificationVisibilityChanged(String key, long time, boolean visible)241         public void onNotificationVisibilityChanged(String key, long time, boolean visible) {
242             SomeArgs args = SomeArgs.obtain();
243             args.arg1 = key;
244             args.arg2 = time;
245             args.argi1 = visible ? 1 : 0;
246             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_VISIBILITY_CHANGED,
247                     args).sendToTarget();
248         }
249 
250         @Override
onNotificationClick(String key, long time)251         public void onNotificationClick(String key, long time) {
252             SomeArgs args = SomeArgs.obtain();
253             args.arg1 = key;
254             args.arg2 = time;
255             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_CLICK,
256                     args).sendToTarget();
257         }
258 
259         @Override
onNotificationActionClick(String key, long time, int actionIndex)260         public void onNotificationActionClick(String key, long time, int actionIndex) {
261             SomeArgs args = SomeArgs.obtain();
262             args.arg1 = key;
263             args.arg2 = time;
264             args.argi1 = actionIndex;
265             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ACTION_CLICK,
266                     args).sendToTarget();
267         }
268 
269         @Override
onNotificationRemovedReason(String key, long time, int reason)270         public void onNotificationRemovedReason(String key, long time, int reason) {
271             SomeArgs args = SomeArgs.obtain();
272             args.arg1 = key;
273             args.arg2 = time;
274             args.argi1 = reason;
275             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED_REASON,
276                     args).sendToTarget();
277         }
278     }
279 
280     private final class MyHandler extends Handler {
281         public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
282         public static final int MSG_ON_NOTIFICATION_VISIBILITY_CHANGED = 2;
283         public static final int MSG_ON_NOTIFICATION_CLICK = 3;
284         public static final int MSG_ON_NOTIFICATION_ACTION_CLICK = 4;
285         public static final int MSG_ON_NOTIFICATION_REMOVED_REASON = 5;
286 
MyHandler(Looper looper)287         public MyHandler(Looper looper) {
288             super(looper, null, false);
289         }
290 
291         @Override
handleMessage(Message msg)292         public void handleMessage(Message msg) {
293             switch (msg.what) {
294                 case MSG_ON_NOTIFICATION_ENQUEUED: {
295                     SomeArgs args = (SomeArgs) msg.obj;
296                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
297                     final int importance = args.argi1;
298                     final boolean user = args.argi2 == 1;
299                     args.recycle();
300                     Adjustment adjustment = onNotificationEnqueued(sbn, importance, user);
301                     if (adjustment != null) {
302                         adjustNotification(adjustment);
303                     }
304                 } break;
305 
306                 case MSG_ON_NOTIFICATION_VISIBILITY_CHANGED: {
307                     SomeArgs args = (SomeArgs) msg.obj;
308                     final String key = (String) args.arg1;
309                     final long time = (long) args.arg2;
310                     final boolean visible = args.argi1 == 1;
311                     args.recycle();
312                     onNotificationVisibilityChanged(key, time, visible);
313                 } break;
314 
315                 case MSG_ON_NOTIFICATION_CLICK: {
316                     SomeArgs args = (SomeArgs) msg.obj;
317                     final String key = (String) args.arg1;
318                     final long time = (long) args.arg2;
319                     args.recycle();
320                     onNotificationClick(key, time);
321                 } break;
322 
323                 case MSG_ON_NOTIFICATION_ACTION_CLICK: {
324                     SomeArgs args = (SomeArgs) msg.obj;
325                     final String key = (String) args.arg1;
326                     final long time = (long) args.arg2;
327                     final int actionIndex = args.argi1;
328                     args.recycle();
329                     onNotificationActionClick(key, time, actionIndex);
330                 } break;
331 
332                 case MSG_ON_NOTIFICATION_REMOVED_REASON: {
333                     SomeArgs args = (SomeArgs) msg.obj;
334                     final String key = (String) args.arg1;
335                     final long time = (long) args.arg2;
336                     final int reason = args.argi1;
337                     args.recycle();
338                     onNotificationRemoved(key, time, reason);
339                 } break;
340             }
341         }
342     }
343 }
344