File: NotificationRankerService.java

package info (click to toggle)
android-platform-frameworks-base 1%3A7.0.0%2Br33-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 137,116 kB
  • sloc: java: 704,370; cpp: 206,670; xml: 185,778; python: 2,626; ansic: 486; sh: 291; makefile: 48; sed: 19
file content (343 lines) | stat: -rw-r--r-- 13,249 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.service.notification;

import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.os.SomeArgs;

import java.util.List;

/**
 * A service that helps the user manage notifications. This class is only used to
 * extend the framework service and may not be implemented by non-framework components.
 * @hide
 */
@SystemApi
public abstract class NotificationRankerService extends NotificationListenerService {
    private static final String TAG = "NotificationRankers";

    /**
     * The {@link Intent} that must be declared as handled by the service.
     */
    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
    public static final String SERVICE_INTERFACE
            = "android.service.notification.NotificationRankerService";

    /** Notification was canceled by the status bar reporting a click. */
    public static final int REASON_DELEGATE_CLICK = 1;

    /** Notification was canceled by the status bar reporting a user dismissal. */
    public static final int REASON_DELEGATE_CANCEL = 2;

    /** Notification was canceled by the status bar reporting a user dismiss all. */
    public static final int REASON_DELEGATE_CANCEL_ALL = 3;

    /** Notification was canceled by the status bar reporting an inflation error. */
    public static final int REASON_DELEGATE_ERROR = 4;

    /** Notification was canceled by the package manager modifying the package. */
    public static final int REASON_PACKAGE_CHANGED = 5;

    /** Notification was canceled by the owning user context being stopped. */
    public static final int REASON_USER_STOPPED = 6;

    /** Notification was canceled by the user banning the package. */
    public static final int REASON_PACKAGE_BANNED = 7;

    /** Notification was canceled by the app canceling this specific notification. */
    public static final int REASON_APP_CANCEL = 8;

    /** Notification was canceled by the app cancelling all its notifications. */
    public static final int REASON_APP_CANCEL_ALL = 9;

    /** Notification was canceled by a listener reporting a user dismissal. */
    public static final int REASON_LISTENER_CANCEL = 10;

    /** Notification was canceled by a listener reporting a user dismiss all. */
    public static final int REASON_LISTENER_CANCEL_ALL = 11;

    /** Notification was canceled because it was a member of a canceled group. */
    public static final int REASON_GROUP_SUMMARY_CANCELED = 12;

    /** Notification was canceled because it was an invisible member of a group. */
    public static final int REASON_GROUP_OPTIMIZATION = 13;

    /** Notification was canceled by the device administrator suspending the package. */
    public static final int REASON_PACKAGE_SUSPENDED = 14;

    /** Notification was canceled by the owning managed profile being turned off. */
    public static final int REASON_PROFILE_TURNED_OFF = 15;

    /** Autobundled summary notification was canceled because its group was unbundled */
    public static final int REASON_UNAUTOBUNDLED = 16;

    private Handler mHandler;

    /** @hide */
    @Override
    public void registerAsSystemService(Context context, ComponentName componentName,
            int currentUser)  {
        throw new UnsupportedOperationException("the ranker lifecycle is managed by the system.");
    }

    /** @hide */
    @Override
    public void unregisterAsSystemService()  {
        throw new UnsupportedOperationException("the ranker lifecycle is managed by the system.");
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        mHandler = new MyHandler(getContext().getMainLooper());
    }

    @Override
    public final IBinder onBind(Intent intent) {
        if (mWrapper == null) {
            mWrapper = new NotificationRankingServiceWrapper();
        }
        return mWrapper;
    }

    /**
     * A notification was posted by an app. Called before alert.
     *
     * @param sbn the new notification
     * @param importance the initial importance of the notification.
     * @param user true if the initial importance reflects an explicit user preference.
     * @return an adjustment or null to take no action, within 100ms.
     */
    abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
          int importance, boolean user);

    /**
     * The visibility of a notification has changed.
     *
     * @param key the notification key
     * @param time milliseconds since midnight, January 1, 1970 UTC.
     * @param visible true if the notification became visible, false if hidden.
     */
    public void onNotificationVisibilityChanged(String key, long time, boolean visible)
    {
        // Do nothing, Override this to collect visibility statistics.
    }

    /**
     * The user clicked on a notification.
     *
     * @param key the notification key
     * @param time milliseconds since midnight, January 1, 1970 UTC.
     */
    public void onNotificationClick(String key, long time)
    {
        // Do nothing, Override this to collect click statistics
    }

    /**
     * The user clicked on a notification action.
     *
     * @param key the notification key
     * @param time milliseconds since midnight, January 1, 1970 UTC.
     * @param actionIndex the index of the action button that was pressed.
     */
    public void onNotificationActionClick(String key, long time, int actionIndex)
    {
        // Do nothing, Override this to collect action button click statistics
    }

    /**
     * A notification was removed.

     * @param key the notification key
     * @param time milliseconds since midnight, January 1, 1970 UTC.
     * @param reason see {@link #REASON_LISTENER_CANCEL}, etc.
     */
    public void onNotificationRemoved(String key, long time, int reason) {
        // Do nothing, Override this to collect dismissal statistics
    }

    /**
     * Updates a notification.  N.B. this won’t cause
     * an existing notification to alert, but might allow a future update to
     * this notification to alert.
     *
     * @param adjustment the adjustment with an explanation
     */
    public final void adjustNotification(Adjustment adjustment) {
        if (!isBound()) return;
        try {
            getNotificationInterface().applyAdjustmentFromRankerService(mWrapper, adjustment);
        } catch (android.os.RemoteException ex) {
            Log.v(TAG, "Unable to contact notification manager", ex);
        }
    }

    /**
     * Updates existing notifications. Re-ranking won't occur until all adjustments are applied.
     * N.B. this won’t cause an existing notification to alert, but might allow a future update to
     * these notifications to alert.
     *
     * @param adjustments a list of adjustments with explanations
     */
    public final void adjustNotifications(List<Adjustment> adjustments) {
        if (!isBound()) return;
        try {
            getNotificationInterface().applyAdjustmentsFromRankerService(mWrapper, adjustments);
        } catch (android.os.RemoteException ex) {
            Log.v(TAG, "Unable to contact notification manager", ex);
        }
    }

    private class NotificationRankingServiceWrapper extends NotificationListenerWrapper {
        @Override
        public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
                int importance, boolean user) {
            StatusBarNotification sbn;
            try {
                sbn = sbnHolder.get();
            } catch (RemoteException e) {
                Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e);
                return;
            }

            SomeArgs args = SomeArgs.obtain();
            args.arg1 = sbn;
            args.argi1 = importance;
            args.argi2 = user ? 1 : 0;
            mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED,
                    args).sendToTarget();
        }

        @Override
        public void onNotificationVisibilityChanged(String key, long time, boolean visible) {
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = key;
            args.arg2 = time;
            args.argi1 = visible ? 1 : 0;
            mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_VISIBILITY_CHANGED,
                    args).sendToTarget();
        }

        @Override
        public void onNotificationClick(String key, long time) {
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = key;
            args.arg2 = time;
            mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_CLICK,
                    args).sendToTarget();
        }

        @Override
        public void onNotificationActionClick(String key, long time, int actionIndex) {
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = key;
            args.arg2 = time;
            args.argi1 = actionIndex;
            mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ACTION_CLICK,
                    args).sendToTarget();
        }

        @Override
        public void onNotificationRemovedReason(String key, long time, int reason) {
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = key;
            args.arg2 = time;
            args.argi1 = reason;
            mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED_REASON,
                    args).sendToTarget();
        }
    }

    private final class MyHandler extends Handler {
        public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
        public static final int MSG_ON_NOTIFICATION_VISIBILITY_CHANGED = 2;
        public static final int MSG_ON_NOTIFICATION_CLICK = 3;
        public static final int MSG_ON_NOTIFICATION_ACTION_CLICK = 4;
        public static final int MSG_ON_NOTIFICATION_REMOVED_REASON = 5;

        public MyHandler(Looper looper) {
            super(looper, null, false);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ON_NOTIFICATION_ENQUEUED: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    StatusBarNotification sbn = (StatusBarNotification) args.arg1;
                    final int importance = args.argi1;
                    final boolean user = args.argi2 == 1;
                    args.recycle();
                    Adjustment adjustment = onNotificationEnqueued(sbn, importance, user);
                    if (adjustment != null) {
                        adjustNotification(adjustment);
                    }
                } break;

                case MSG_ON_NOTIFICATION_VISIBILITY_CHANGED: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    final String key = (String) args.arg1;
                    final long time = (long) args.arg2;
                    final boolean visible = args.argi1 == 1;
                    args.recycle();
                    onNotificationVisibilityChanged(key, time, visible);
                } break;

                case MSG_ON_NOTIFICATION_CLICK: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    final String key = (String) args.arg1;
                    final long time = (long) args.arg2;
                    args.recycle();
                    onNotificationClick(key, time);
                } break;

                case MSG_ON_NOTIFICATION_ACTION_CLICK: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    final String key = (String) args.arg1;
                    final long time = (long) args.arg2;
                    final int actionIndex = args.argi1;
                    args.recycle();
                    onNotificationActionClick(key, time, actionIndex);
                } break;

                case MSG_ON_NOTIFICATION_REMOVED_REASON: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    final String key = (String) args.arg1;
                    final long time = (long) args.arg2;
                    final int reason = args.argi1;
                    args.recycle();
                    onNotificationRemoved(key, time, reason);
                } break;
            }
        }
    }
}