[go: nahoru, domu]

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
17package android.service.notification;
18
19import android.annotation.SdkConstant;
20import android.annotation.SystemApi;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.net.Uri;
25import android.os.Bundle;
26import android.os.Handler;
27import android.os.IBinder;
28import android.os.Looper;
29import android.os.Message;
30import android.os.Parcel;
31import android.os.Parcelable;
32import android.os.RemoteException;
33import android.util.Log;
34import com.android.internal.os.SomeArgs;
35
36import 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
44public 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
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
113    public void unregisterAsSystemService()  {
114        throw new UnsupportedOperationException("the ranker lifecycle is managed by the system.");
115    }
116
117    @Override
118    protected void attachBaseContext(Context base) {
119        super.attachBaseContext(base);
120        mHandler = new MyHandler(getContext().getMainLooper());
121    }
122
123    @Override
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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
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
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
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
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
287        public MyHandler(Looper looper) {
288            super(looper, null, false);
289        }
290
291        @Override
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