1 /*
2  * Copyright (C) 2013 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.CurrentTimeMillisLong;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SdkConstant;
24 import android.annotation.SystemApi;
25 import android.annotation.UiThread;
26 import android.app.ActivityManager;
27 import android.app.INotificationManager;
28 import android.app.Notification;
29 import android.app.Notification.Builder;
30 import android.app.NotificationChannel;
31 import android.app.NotificationChannelGroup;
32 import android.app.NotificationManager;
33 import android.app.Person;
34 import android.app.Service;
35 import android.companion.CompanionDeviceManager;
36 import android.compat.annotation.UnsupportedAppUsage;
37 import android.content.ComponentName;
38 import android.content.Context;
39 import android.content.Intent;
40 import android.content.pm.ParceledListSlice;
41 import android.content.pm.ShortcutInfo;
42 import android.graphics.Bitmap;
43 import android.graphics.drawable.BitmapDrawable;
44 import android.graphics.drawable.Drawable;
45 import android.graphics.drawable.Icon;
46 import android.os.BadParcelableException;
47 import android.os.Build;
48 import android.os.Bundle;
49 import android.os.Handler;
50 import android.os.IBinder;
51 import android.os.Looper;
52 import android.os.Message;
53 import android.os.Parcel;
54 import android.os.Parcelable;
55 import android.os.RemoteException;
56 import android.os.ServiceManager;
57 import android.os.UserHandle;
58 import android.util.ArrayMap;
59 import android.util.Log;
60 import android.widget.RemoteViews;
61 
62 import com.android.internal.annotations.GuardedBy;
63 import com.android.internal.annotations.VisibleForTesting;
64 import com.android.internal.os.SomeArgs;
65 
66 import java.lang.annotation.Retention;
67 import java.lang.annotation.RetentionPolicy;
68 import java.util.ArrayList;
69 import java.util.Collections;
70 import java.util.List;
71 import java.util.Objects;
72 
73 /**
74  * A service that receives calls from the system when new notifications are
75  * posted or removed, or their ranking changed.
76  * <p>To extend this class, you must declare the service in your manifest file with
77  * the {@link android.Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE} permission
78  * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
79  * <pre>
80  * &lt;service android:name=".NotificationListener"
81  *          android:label="&#64;string/service_name"
82  *          android:exported="false"
83  *          android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
84  *     &lt;intent-filter>
85  *         &lt;action android:name="android.service.notification.NotificationListenerService" />
86  *     &lt;/intent-filter>
87  *     &lt;meta-data
88  *               android:name="android.service.notification.default_filter_types"
89  *               android:value="conversations|alerting">
90  *           &lt;/meta-data>
91  *     &lt;meta-data
92  *               android:name="android.service.notification.disabled_filter_types"
93  *               android:value="ongoing|silent">
94  *           &lt;/meta-data>
95  * &lt;/service></pre>
96  *
97  * <p>The service should wait for the {@link #onListenerConnected()} event
98  * before performing any operations. The {@link #requestRebind(ComponentName)}
99  * method is the <i>only</i> one that is safe to call before {@link #onListenerConnected()}
100  * or after {@link #onListenerDisconnected()}.
101  * </p>
102  * <p> Notification listeners cannot get notification access or be bound by the system on
103  * {@linkplain ActivityManager#isLowRamDevice() low-RAM} devices running Android Q (and below).
104  * The system also ignores notification listeners running in a work profile. A
105  * {@link android.app.admin.DevicePolicyManager} might block notifications originating from a work
106  * profile.</p>
107  * <p>
108  *     From {@link Build.VERSION_CODES#N} onward all callbacks are called on the main thread. Prior
109  *     to N, there is no guarantee on what thread the callback will happen.
110  * </p>
111  */
112 public abstract class NotificationListenerService extends Service {
113 
114     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
115     private final String TAG = getClass().getSimpleName();
116 
117     /**
118      * The name of the {@code meta-data} tag containing a pipe separated list of default
119      * integer notification types or "ongoing", "conversations", "alerting", or "silent"
120      * that should be provided to this listener. See
121      * {@link #FLAG_FILTER_TYPE_ONGOING},
122      * {@link #FLAG_FILTER_TYPE_CONVERSATIONS}, {@link #FLAG_FILTER_TYPE_ALERTING),
123      * and {@link #FLAG_FILTER_TYPE_SILENT}.
124      * <p>This value will only be read if the app has not previously specified a default type list,
125      * and if the user has not overridden the allowed types.</p>
126      * <p>An absent value means 'allow all types'.
127      * A present but empty value means 'allow no types'.</p>
128      *
129      */
130     public static final String META_DATA_DEFAULT_FILTER_TYPES
131             = "android.service.notification.default_filter_types";
132 
133     /**
134      * The name of the {@code meta-data} tag containing a comma separated list of default
135      * integer notification types that this listener never wants to receive. See
136      * {@link #FLAG_FILTER_TYPE_ONGOING},
137      * {@link #FLAG_FILTER_TYPE_CONVERSATIONS}, {@link #FLAG_FILTER_TYPE_ALERTING),
138      * and {@link #FLAG_FILTER_TYPE_SILENT}.
139      * <p>Types provided in this list will appear as 'off' and 'disabled' in the user interface,
140      * so users don't enable a type that the listener will never bridge to their paired devices.</p>
141      *
142      */
143     public static final String META_DATA_DISABLED_FILTER_TYPES
144             = "android.service.notification.disabled_filter_types";
145 
146     /**
147      * The name of the {@code meta-data} tag containing a boolean value that is used to decide if
148      * this listener should be automatically bound by default.
149      * If the value is 'false', the listener can be bound on demand using {@link #requestRebind}
150      * <p>An absent value means that the default is 'true'</p>
151      *
152      */
153     public static final String META_DATA_DEFAULT_AUTOBIND
154             = "android.service.notification.default_autobind_listenerservice";
155 
156     /**
157      * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
158      *     Normal interruption filter.
159      */
160     public static final int INTERRUPTION_FILTER_ALL
161             = NotificationManager.INTERRUPTION_FILTER_ALL;
162 
163     /**
164      * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
165      *     Priority interruption filter.
166      */
167     public static final int INTERRUPTION_FILTER_PRIORITY
168             = NotificationManager.INTERRUPTION_FILTER_PRIORITY;
169 
170     /**
171      * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
172      *     No interruptions filter.
173      */
174     public static final int INTERRUPTION_FILTER_NONE
175             = NotificationManager.INTERRUPTION_FILTER_NONE;
176 
177     /**
178      * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
179      *     Alarms only interruption filter.
180      */
181     public static final int INTERRUPTION_FILTER_ALARMS
182             = NotificationManager.INTERRUPTION_FILTER_ALARMS;
183 
184     /** {@link #getCurrentInterruptionFilter() Interruption filter} constant - returned when
185      * the value is unavailable for any reason.  For example, before the notification listener
186      * is connected.
187      *
188      * {@see #onListenerConnected()}
189      */
190     public static final int INTERRUPTION_FILTER_UNKNOWN
191             = NotificationManager.INTERRUPTION_FILTER_UNKNOWN;
192 
193     /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
194      * should disable notification sound, vibrating and other visual or aural effects.
195      * This does not change the interruption filter, only the effects. **/
196     public static final int HINT_HOST_DISABLE_EFFECTS = 1;
197 
198     /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
199      * should disable notification sound, but not phone calls.
200      * This does not change the interruption filter, only the effects. **/
201     public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 1 << 1;
202 
203     /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
204      * should disable phone call sounds, but not notification sound.
205      * This does not change the interruption filter, only the effects. **/
206     public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 1 << 2;
207 
208     /**
209      * Whether notification suppressed by DND should not interruption visually when the screen is
210      * off.
211      *
212      * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}.
213      */
214     @Deprecated
215     public static final int SUPPRESSED_EFFECT_SCREEN_OFF =
216             NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
217     /**
218      * Whether notification suppressed by DND should not interruption visually when the screen is
219      * on.
220      *
221      * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}.
222      */
223     @Deprecated
224     public static final int SUPPRESSED_EFFECT_SCREEN_ON =
225             NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
226 
227 
228     // Notification cancellation reasons
229 
230     /** Notification was canceled by the status bar reporting a notification click. */
231     public static final int REASON_CLICK = 1;
232     /** Notification was canceled by the status bar reporting a user dismissal. */
233     public static final int REASON_CANCEL = 2;
234     /** Notification was canceled by the status bar reporting a user dismiss all. */
235     public static final int REASON_CANCEL_ALL = 3;
236     /** Notification was canceled by the status bar reporting an inflation error. */
237     public static final int REASON_ERROR = 4;
238     /** Notification was canceled by the package manager modifying the package. */
239     public static final int REASON_PACKAGE_CHANGED = 5;
240     /** Notification was canceled by the owning user context being stopped. */
241     public static final int REASON_USER_STOPPED = 6;
242     /** Notification was canceled by the user banning the package. */
243     public static final int REASON_PACKAGE_BANNED = 7;
244     /** Notification was canceled by the app canceling this specific notification. */
245     public static final int REASON_APP_CANCEL = 8;
246     /** Notification was canceled by the app cancelling all its notifications. */
247     public static final int REASON_APP_CANCEL_ALL = 9;
248     /** Notification was canceled by a listener reporting a user dismissal. */
249     public static final int REASON_LISTENER_CANCEL = 10;
250     /** Notification was canceled by a listener reporting a user dismiss all. */
251     public static final int REASON_LISTENER_CANCEL_ALL = 11;
252     /** Notification was canceled because it was a member of a canceled group. */
253     public static final int REASON_GROUP_SUMMARY_CANCELED = 12;
254     /** Notification was canceled because it was an invisible member of a group. */
255     public static final int REASON_GROUP_OPTIMIZATION = 13;
256     /** Notification was canceled by the device administrator suspending the package. */
257     public static final int REASON_PACKAGE_SUSPENDED = 14;
258     /** Notification was canceled by the owning managed profile being turned off. */
259     public static final int REASON_PROFILE_TURNED_OFF = 15;
260     /** Autobundled summary notification was canceled because its group was unbundled */
261     public static final int REASON_UNAUTOBUNDLED = 16;
262     /** Notification was canceled by the user banning the channel. */
263     public static final int REASON_CHANNEL_BANNED = 17;
264     /** Notification was snoozed. */
265     public static final int REASON_SNOOZED = 18;
266     /** Notification was canceled due to timeout */
267     public static final int REASON_TIMEOUT = 19;
268     /** Notification was canceled due to the backing channel being deleted */
269     public static final int REASON_CHANNEL_REMOVED = 20;
270     /** Notification was canceled due to the app's storage being cleared */
271     public static final int REASON_CLEAR_DATA = 21;
272     /** Notification was canceled due to an assistant adjustment update. */
273     public static final int REASON_ASSISTANT_CANCEL = 22;
274     /**
275      * Notification was canceled when entering lockdown mode, which turns off
276      * Smart Lock, fingerprint unlocking, and notifications on the lock screen.
277      * All the listeners shall ensure the canceled notifications are indeed removed
278      * on their end to prevent data leaking.
279      * When the user exits the lockdown mode, the removed notifications (due to lockdown)
280      * will be restored via NotificationListeners#notifyPostedLocked()
281      */
282     public static final int REASON_LOCKDOWN = 23;
283     // If adding a new notification cancellation reason, you must also add handling for it in
284     // NotificationCancelledEvent.fromCancelReason.
285 
286     /**
287      * @hide
288      */
289     @IntDef(prefix = "REASON_", value = {
290             REASON_CLICK,
291             REASON_CANCEL,
292             REASON_CANCEL_ALL,
293             REASON_ERROR,
294             REASON_PACKAGE_CHANGED,
295             REASON_USER_STOPPED,
296             REASON_PACKAGE_BANNED,
297             REASON_APP_CANCEL,
298             REASON_APP_CANCEL_ALL,
299             REASON_LISTENER_CANCEL,
300             REASON_LISTENER_CANCEL_ALL,
301             REASON_GROUP_SUMMARY_CANCELED,
302             REASON_GROUP_OPTIMIZATION,
303             REASON_PACKAGE_SUSPENDED,
304             REASON_PROFILE_TURNED_OFF,
305             REASON_UNAUTOBUNDLED,
306             REASON_CHANNEL_BANNED,
307             REASON_SNOOZED,
308             REASON_TIMEOUT,
309             REASON_CHANNEL_REMOVED,
310             REASON_CLEAR_DATA,
311             REASON_ASSISTANT_CANCEL,
312             REASON_LOCKDOWN,
313     })
314     @Retention(RetentionPolicy.SOURCE)
315     public @interface NotificationCancelReason{};
316 
317     /**
318      * @hide
319      */
320     @IntDef(flag = true, prefix = { "FLAG_FILTER_TYPE_" }, value = {
321             FLAG_FILTER_TYPE_CONVERSATIONS,
322             FLAG_FILTER_TYPE_ALERTING,
323             FLAG_FILTER_TYPE_SILENT,
324             FLAG_FILTER_TYPE_ONGOING
325     })
326     @Retention(RetentionPolicy.SOURCE)
327     public @interface NotificationFilterTypes {}
328     /**
329      * A flag value indicating that this notification listener can see conversation type
330      * notifications.
331      */
332     public static final int FLAG_FILTER_TYPE_CONVERSATIONS = 1;
333     /**
334      * A flag value indicating that this notification listener can see altering type notifications.
335      */
336     public static final int FLAG_FILTER_TYPE_ALERTING = 2;
337     /**
338      * A flag value indicating that this notification listener can see silent type notifications.
339      */
340     public static final int FLAG_FILTER_TYPE_SILENT = 4;
341     /**
342      * A flag value indicating that this notification listener can see important
343      * ( > {@link NotificationManager#IMPORTANCE_MIN}) ongoing type notifications.
344      */
345     public static final int FLAG_FILTER_TYPE_ONGOING = 8;
346 
347     /**
348      * The full trim of the StatusBarNotification including all its features.
349      *
350      * @hide
351      * @removed
352      */
353     @SystemApi
354     public static final int TRIM_FULL = 0;
355 
356     /**
357      * A light trim of the StatusBarNotification excluding the following features:
358      *
359      * <ol>
360      *     <li>{@link Notification#tickerView tickerView}</li>
361      *     <li>{@link Notification#contentView contentView}</li>
362      *     <li>{@link Notification#largeIcon largeIcon}</li>
363      *     <li>{@link Notification#bigContentView bigContentView}</li>
364      *     <li>{@link Notification#headsUpContentView headsUpContentView}</li>
365      *     <li>{@link Notification#EXTRA_LARGE_ICON extras[EXTRA_LARGE_ICON]}</li>
366      *     <li>{@link Notification#EXTRA_LARGE_ICON_BIG extras[EXTRA_LARGE_ICON_BIG]}</li>
367      *     <li>{@link Notification#EXTRA_PICTURE extras[EXTRA_PICTURE]}</li>
368      *     <li>{@link Notification#EXTRA_BIG_TEXT extras[EXTRA_BIG_TEXT]}</li>
369      * </ol>
370      *
371      * @hide
372      * @removed
373      */
374     @SystemApi
375     public static final int TRIM_LIGHT = 1;
376 
377 
378     /** @hide */
379     @IntDef(prefix = { "NOTIFICATION_CHANNEL_OR_GROUP_" }, value = {
380             NOTIFICATION_CHANNEL_OR_GROUP_ADDED,
381             NOTIFICATION_CHANNEL_OR_GROUP_UPDATED,
382             NOTIFICATION_CHANNEL_OR_GROUP_DELETED
383     })
384     @Retention(RetentionPolicy.SOURCE)
385     public @interface ChannelOrGroupModificationTypes {}
386 
387     /**
388      * Channel or group modification reason provided to
389      * {@link #onNotificationChannelModified(String, UserHandle,NotificationChannel, int)} or
390      * {@link #onNotificationChannelGroupModified(String, UserHandle, NotificationChannelGroup,
391      * int)}- the provided object was created.
392      */
393     public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1;
394 
395     /**
396      * Channel or group modification reason provided to
397      * {@link #onNotificationChannelModified(String, UserHandle, NotificationChannel, int)} or
398      * {@link #onNotificationChannelGroupModified(String, UserHandle,NotificationChannelGroup, int)}
399      * - the provided object was updated.
400      */
401     public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2;
402 
403     /**
404      * Channel or group modification reason provided to
405      * {@link #onNotificationChannelModified(String, UserHandle, NotificationChannel, int)} or
406      * {@link #onNotificationChannelGroupModified(String, UserHandle, NotificationChannelGroup,
407      * int)}- the provided object was deleted.
408      */
409     public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3;
410 
411     /**
412      * An optional activity intent action that shows additional settings for what notifications
413      * should be processed by this notification listener service. If defined, the OS may link to
414      * this activity from the system notification listener service filter settings page.
415      */
416     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
417     public static final String ACTION_SETTINGS_HOME =
418             "android.service.notification.action.SETTINGS_HOME";
419 
420     private final Object mLock = new Object();
421 
422     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
423     private Handler mHandler;
424 
425     /** @hide */
426     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
427     protected NotificationListenerWrapper mWrapper = null;
428     private boolean isConnected = false;
429 
430     @GuardedBy("mLock")
431     private RankingMap mRankingMap;
432 
433     /**
434      * @hide
435      */
436     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
437     protected INotificationManager mNoMan;
438 
439     /**
440      * Only valid after a successful call to (@link registerAsService}.
441      * @hide
442      */
443     protected int mCurrentUser;
444 
445     /**
446      * This context is required for system services since NotificationListenerService isn't
447      * started as a real Service and hence no context is available..
448      * @hide
449      */
450     protected Context mSystemContext;
451 
452     /**
453      * The {@link Intent} that must be declared as handled by the service.
454      */
455     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
456     public static final String SERVICE_INTERFACE
457             = "android.service.notification.NotificationListenerService";
458 
459     @Override
attachBaseContext(Context base)460     protected void attachBaseContext(Context base) {
461         super.attachBaseContext(base);
462         mHandler = new MyHandler(getMainLooper());
463     }
464 
465     /**
466      * Implement this method to learn about new notifications as they are posted by apps.
467      *
468      * @param sbn A data structure encapsulating the original {@link android.app.Notification}
469      *            object as well as its identifying information (tag and id) and source
470      *            (package name).
471      */
472     @UiThread
onNotificationPosted(StatusBarNotification sbn)473     public void onNotificationPosted(StatusBarNotification sbn) {
474         // optional
475     }
476 
477     /**
478      * Implement this method to learn about new notifications as they are posted by apps.
479      *
480      * @param sbn A data structure encapsulating the original {@link android.app.Notification}
481      *            object as well as its identifying information (tag and id) and source
482      *            (package name).
483      * @param rankingMap The current ranking map that can be used to retrieve ranking information
484      *                   for active notifications, including the newly posted one.
485      */
486     @UiThread
onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap)487     public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
488         onNotificationPosted(sbn);
489     }
490 
491     /**
492      * Implement this method to learn when notifications are removed.
493      * <p>
494      * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
495      * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
496      * fields such as {@link android.app.Notification#contentView} and
497      * {@link android.app.Notification#largeIcon}. However, all other fields on
498      * {@link StatusBarNotification}, sufficient to match this call with a prior call to
499      * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
500      *
501      * @param sbn A data structure encapsulating at least the original information (tag and id)
502      *            and source (package name) used to post the {@link android.app.Notification} that
503      *            was just removed.
504      */
505     @UiThread
onNotificationRemoved(StatusBarNotification sbn)506     public void onNotificationRemoved(StatusBarNotification sbn) {
507         // optional
508     }
509 
510     /**
511      * Implement this method to learn when notifications are removed.
512      * <p>
513      * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
514      * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
515      * fields such as {@link android.app.Notification#contentView} and
516      * {@link android.app.Notification#largeIcon}. However, all other fields on
517      * {@link StatusBarNotification}, sufficient to match this call with a prior call to
518      * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
519      *
520      * @param sbn A data structure encapsulating at least the original information (tag and id)
521      *            and source (package name) used to post the {@link android.app.Notification} that
522      *            was just removed.
523      * @param rankingMap The current ranking map that can be used to retrieve ranking information
524      *                   for active notifications.
525      *
526      */
527     @UiThread
onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap)528     public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
529         onNotificationRemoved(sbn);
530     }
531 
532 
533     /**
534      * Implement this method to learn when notifications are removed and why.
535      * <p>
536      * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
537      * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
538      * fields such as {@link android.app.Notification#contentView} and
539      * {@link android.app.Notification#largeIcon}. However, all other fields on
540      * {@link StatusBarNotification}, sufficient to match this call with a prior call to
541      * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
542      *
543      ** @param sbn A data structure encapsulating at least the original information (tag and id)
544      *            and source (package name) used to post the {@link android.app.Notification} that
545      *            was just removed.
546      * @param rankingMap The current ranking map that can be used to retrieve ranking information
547      *                   for active notifications.
548      */
549     @UiThread
onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, @NotificationCancelReason int reason)550     public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
551             @NotificationCancelReason int reason) {
552         onNotificationRemoved(sbn, rankingMap);
553     }
554 
555     /**
556      * NotificationStats are not populated for notification listeners, so fall back to
557      * {@link #onNotificationRemoved(StatusBarNotification, RankingMap, int)}.
558      *
559      * @hide
560      */
561     @UiThread
562     @SystemApi
onNotificationRemoved(@onNull StatusBarNotification sbn, @NonNull RankingMap rankingMap, @NonNull NotificationStats stats, int reason)563     public void onNotificationRemoved(@NonNull StatusBarNotification sbn,
564             @NonNull RankingMap rankingMap, @NonNull NotificationStats stats, int reason) {
565         onNotificationRemoved(sbn, rankingMap, reason);
566     }
567 
568     /**
569      * Implement this method to learn about when the listener is enabled and connected to
570      * the notification manager.  You are safe to call {@link #getActiveNotifications()}
571      * at this time.
572      */
573     @UiThread
onListenerConnected()574     public void onListenerConnected() {
575         // optional
576     }
577 
578     /**
579      * Implement this method to learn about when the listener is disconnected from the
580      * notification manager.You will not receive any events after this call, and may only
581      * call {@link #requestRebind(ComponentName)} at this time.
582      */
583     @UiThread
onListenerDisconnected()584     public void onListenerDisconnected() {
585         // optional
586     }
587 
588     /**
589      * Implement this method to be notified when the notification ranking changes.
590      *
591      * @param rankingMap The current ranking map that can be used to retrieve ranking information
592      *                   for active notifications.
593      */
594     @UiThread
onNotificationRankingUpdate(RankingMap rankingMap)595     public void onNotificationRankingUpdate(RankingMap rankingMap) {
596         // optional
597     }
598 
599     /**
600      * Implement this method to be notified when the
601      * {@link #getCurrentListenerHints() Listener hints} change.
602      *
603      * @param hints The current {@link #getCurrentListenerHints() listener hints}.
604      */
605     @UiThread
onListenerHintsChanged(int hints)606     public void onListenerHintsChanged(int hints) {
607         // optional
608     }
609 
610     /**
611      * Implement this method to be notified when the behavior of silent notifications in the status
612      * bar changes. See {@link NotificationManager#shouldHideSilentStatusBarIcons()}.
613      *
614      * @param hideSilentStatusIcons whether or not status bar icons should be hidden for silent
615      *                              notifications
616      */
617     @UiThread
onSilentStatusBarIconsVisibilityChanged(boolean hideSilentStatusIcons)618     public void onSilentStatusBarIconsVisibilityChanged(boolean hideSilentStatusIcons) {
619         // optional
620     }
621 
622     /**
623      * Implement this method to learn about notification channel modifications.
624      *
625      * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
626      * device} in order to receive this callback.
627      *
628      * @param pkg The package the channel belongs to.
629      * @param user The user on which the change was made.
630      * @param channel The channel that has changed.
631      * @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED},
632      *                   {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
633      *                   {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
634      */
635     @UiThread
onNotificationChannelModified(String pkg, UserHandle user, NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType)636     public void onNotificationChannelModified(String pkg, UserHandle user,
637             NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType) {
638         // optional
639     }
640 
641     /**
642      * Implement this method to learn about notification channel group modifications.
643      *
644      * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
645      * device} in order to receive this callback.
646      *
647      * @param pkg The package the group belongs to.
648      * @param user The user on which the change was made.
649      * @param group The group that has changed.
650      * @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED},
651      *                   {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
652      *                   {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
653      */
654     @UiThread
onNotificationChannelGroupModified(String pkg, UserHandle user, NotificationChannelGroup group, @ChannelOrGroupModificationTypes int modificationType)655     public void onNotificationChannelGroupModified(String pkg, UserHandle user,
656             NotificationChannelGroup group, @ChannelOrGroupModificationTypes int modificationType) {
657         // optional
658     }
659 
660     /**
661      * Implement this method to be notified when the
662      * {@link #getCurrentInterruptionFilter() interruption filter} changed.
663      *
664      * @param interruptionFilter The current
665      *     {@link #getCurrentInterruptionFilter() interruption filter}.
666      */
667     @UiThread
onInterruptionFilterChanged(int interruptionFilter)668     public void onInterruptionFilterChanged(int interruptionFilter) {
669         // optional
670     }
671 
672     /** @hide */
673     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
getNotificationInterface()674     protected final INotificationManager getNotificationInterface() {
675         if (mNoMan == null) {
676             mNoMan = INotificationManager.Stub.asInterface(
677                     ServiceManager.getService(Context.NOTIFICATION_SERVICE));
678         }
679         return mNoMan;
680     }
681 
682     /**
683      * Inform the notification manager about dismissal of a single notification.
684      * <p>
685      * Use this if your listener has a user interface that allows the user to dismiss individual
686      * notifications, similar to the behavior of Android's status bar and notification panel.
687      * It should be called after the user dismisses a single notification using your UI;
688      * upon being informed, the notification manager will actually remove the notification
689      * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
690      * <p>
691      * <b>Note:</b> If your listener allows the user to fire a notification's
692      * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
693      * this method at that time <i>if</i> the Notification in question has the
694      * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
695      *
696      * <p>The service should wait for the {@link #onListenerConnected()} event
697      * before performing this operation.
698      *
699      * @param pkg Package of the notifying app.
700      * @param tag Tag of the notification as specified by the notifying app in
701      *     {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
702      * @param id  ID of the notification as specified by the notifying app in
703      *     {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
704      * <p>
705      * @deprecated Use {@link #cancelNotification(String key)}
706      * instead. Beginning with {@link android.os.Build.VERSION_CODES#LOLLIPOP} this method will no longer
707      * cancel the notification. It will continue to cancel the notification for applications
708      * whose {@code targetSdkVersion} is earlier than {@link android.os.Build.VERSION_CODES#LOLLIPOP}.
709      */
710     @Deprecated
cancelNotification(String pkg, String tag, int id)711     public final void cancelNotification(String pkg, String tag, int id) {
712         if (!isBound()) return;
713         try {
714             getNotificationInterface().cancelNotificationFromListener(
715                     mWrapper, pkg, tag, id);
716         } catch (android.os.RemoteException ex) {
717             Log.v(TAG, "Unable to contact notification manager", ex);
718         }
719     }
720 
721     /**
722      * Inform the notification manager about dismissal of a single notification.
723      * <p>
724      * Use this if your listener has a user interface that allows the user to dismiss individual
725      * notifications, similar to the behavior of Android's status bar and notification panel.
726      * It should be called after the user dismisses a single notification using your UI;
727      * upon being informed, the notification manager will actually remove the notification
728      * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
729      * <p>
730      * <b>Note:</b> If your listener allows the user to fire a notification's
731      * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
732      * this method at that time <i>if</i> the Notification in question has the
733      * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
734      * <p>
735      *
736      * <p>The service should wait for the {@link #onListenerConnected()} event
737      * before performing this operation.
738      *
739      * @param key Notification to dismiss from {@link StatusBarNotification#getKey()}.
740      */
cancelNotification(String key)741     public final void cancelNotification(String key) {
742         if (!isBound()) return;
743         try {
744             getNotificationInterface().cancelNotificationsFromListener(mWrapper,
745                     new String[] { key });
746         } catch (android.os.RemoteException ex) {
747             Log.v(TAG, "Unable to contact notification manager", ex);
748         }
749     }
750 
751     /**
752      * Inform the notification manager about dismissal of all notifications.
753      * <p>
754      * Use this if your listener has a user interface that allows the user to dismiss all
755      * notifications, similar to the behavior of Android's status bar and notification panel.
756      * It should be called after the user invokes the "dismiss all" function of your UI;
757      * upon being informed, the notification manager will actually remove all active notifications
758      * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks.
759      *
760      * <p>The service should wait for the {@link #onListenerConnected()} event
761      * before performing this operation.
762      *
763      * {@see #cancelNotification(String, String, int)}
764      */
cancelAllNotifications()765     public final void cancelAllNotifications() {
766         cancelNotifications(null /*all*/);
767     }
768 
769     /**
770      * Inform the notification manager about dismissal of specific notifications.
771      * <p>
772      * Use this if your listener has a user interface that allows the user to dismiss
773      * multiple notifications at once.
774      *
775      * <p>The service should wait for the {@link #onListenerConnected()} event
776      * before performing this operation.
777      *
778      * @param keys Notifications to dismiss, or {@code null} to dismiss all.
779      *
780      * {@see #cancelNotification(String, String, int)}
781      */
cancelNotifications(String[] keys)782     public final void cancelNotifications(String[] keys) {
783         if (!isBound()) return;
784         try {
785             getNotificationInterface().cancelNotificationsFromListener(mWrapper, keys);
786         } catch (android.os.RemoteException ex) {
787             Log.v(TAG, "Unable to contact notification manager", ex);
788         }
789     }
790 
791     /**
792      * Inform the notification manager about snoozing a specific notification.
793      * <p>
794      * Use this if your listener has a user interface that allows the user to snooze a notification
795      * until a given {@link SnoozeCriterion}. It should be called after the user snoozes a single
796      * notification using your UI; upon being informed, the notification manager will actually
797      * remove the notification and you will get an
798      * {@link #onNotificationRemoved(StatusBarNotification)} callback. When the snoozing period
799      * expires, you will get a {@link #onNotificationPosted(StatusBarNotification, RankingMap)}
800      * callback for the notification.
801      * @param key The key of the notification to snooze
802      * @param snoozeCriterionId The{@link SnoozeCriterion#getId()} of a context to snooze the
803      *                          notification until.
804      * @hide
805      * @removed
806      */
807     @SystemApi
snoozeNotification(String key, String snoozeCriterionId)808     public final void snoozeNotification(String key, String snoozeCriterionId) {
809         if (!isBound()) return;
810         try {
811             getNotificationInterface().snoozeNotificationUntilContextFromListener(
812                     mWrapper, key, snoozeCriterionId);
813         } catch (android.os.RemoteException ex) {
814             Log.v(TAG, "Unable to contact notification manager", ex);
815         }
816     }
817 
818     /**
819      * Inform the notification manager about snoozing a specific notification.
820      * <p>
821      * Use this if your listener has a user interface that allows the user to snooze a notification
822      * for a time. It should be called after the user snoozes a single notification using
823      * your UI; upon being informed, the notification manager will actually remove the notification
824      * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. When the
825      * snoozing period expires, you will get a
826      * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
827      * notification.
828      * @param key The key of the notification to snooze
829      * @param durationMs A duration to snooze the notification for, in milliseconds.
830      */
snoozeNotification(String key, long durationMs)831     public final void snoozeNotification(String key, long durationMs) {
832         if (!isBound()) return;
833         try {
834             getNotificationInterface().snoozeNotificationUntilFromListener(
835                     mWrapper, key, durationMs);
836         } catch (android.os.RemoteException ex) {
837             Log.v(TAG, "Unable to contact notification manager", ex);
838         }
839     }
840 
841     /**
842      * Lets an app migrate notification filters from its app into the OS.
843      *
844      * <p>This call will be ignored if the app has already migrated these settings or the user
845      * has set filters in the UI. This method is intended for user specific settings; if an app has
846      * already specified defaults types in its manifest with
847      * {@link #META_DATA_DEFAULT_FILTER_TYPES}, the defaultTypes option will be ignored.</p>
848      * @param defaultTypes A value representing the types of notifications that this listener should
849      * receive by default
850      * @param disallowedPkgs A list of package names whose notifications should not be seen by this
851      * listener, by default, because the listener does not process or display them, or because a
852      * user had previously disallowed these packages in the listener app's UI
853      */
migrateNotificationFilter(@otificationFilterTypes int defaultTypes, @Nullable List<String> disallowedPkgs)854     public final void migrateNotificationFilter(@NotificationFilterTypes int defaultTypes,
855             @Nullable List<String> disallowedPkgs) {
856         if (!isBound()) return;
857         try {
858             getNotificationInterface().migrateNotificationFilter(
859                     mWrapper, defaultTypes, disallowedPkgs);
860         } catch (android.os.RemoteException ex) {
861             Log.v(TAG, "Unable to contact notification manager", ex);
862         }
863     }
864 
865     /**
866      * Inform the notification manager that these notifications have been viewed by the
867      * user. This should only be called when there is sufficient confidence that the user is
868      * looking at the notifications, such as when the notifications appear on the screen due to
869      * an explicit user interaction.
870      *
871      * <p>The service should wait for the {@link #onListenerConnected()} event
872      * before performing this operation.
873      *
874      * @param keys Notifications to mark as seen.
875      */
setNotificationsShown(String[] keys)876     public final void setNotificationsShown(String[] keys) {
877         if (!isBound()) return;
878         try {
879             getNotificationInterface().setNotificationsShownFromListener(mWrapper, keys);
880         } catch (android.os.RemoteException ex) {
881             Log.v(TAG, "Unable to contact notification manager", ex);
882         }
883     }
884 
885 
886     /**
887      * Updates a notification channel for a given package for a given user. This should only be used
888      * to reflect changes a user has made to the channel via the listener's user interface.
889      *
890      * <p>This method will throw a security exception if you don't have access to notifications
891      * for the given user.</p>
892      * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
893      * device} in order to use this method.
894      *
895      * @param pkg The package the channel belongs to.
896      * @param user The user the channel belongs to.
897      * @param channel the channel to update.
898      */
updateNotificationChannel(@onNull String pkg, @NonNull UserHandle user, @NonNull NotificationChannel channel)899     public final void updateNotificationChannel(@NonNull String pkg, @NonNull UserHandle user,
900             @NonNull NotificationChannel channel) {
901         if (!isBound()) return;
902         try {
903             getNotificationInterface().updateNotificationChannelFromPrivilegedListener(
904                     mWrapper, pkg, user, channel);
905         } catch (RemoteException e) {
906             Log.v(TAG, "Unable to contact notification manager", e);
907             throw e.rethrowFromSystemServer();
908         }
909     }
910 
911     /**
912      * Returns all notification channels belonging to the given package for a given user.
913      *
914      * <p>This method will throw a security exception if you don't have access to notifications
915      * for the given user.</p>
916      * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
917      * device} or be the notification assistant in order to use this method.
918      *
919      * @param pkg The package to retrieve channels for.
920      */
getNotificationChannels(@onNull String pkg, @NonNull UserHandle user)921     public final List<NotificationChannel> getNotificationChannels(@NonNull String pkg,
922             @NonNull UserHandle user) {
923         if (!isBound()) return null;
924         try {
925 
926             return getNotificationInterface().getNotificationChannelsFromPrivilegedListener(
927                     mWrapper, pkg, user).getList();
928         } catch (RemoteException e) {
929             Log.v(TAG, "Unable to contact notification manager", e);
930             throw e.rethrowFromSystemServer();
931         }
932     }
933 
934     /**
935      * Returns all notification channel groups belonging to the given package for a given user.
936      *
937      * <p>This method will throw a security exception if you don't have access to notifications
938      * for the given user.</p>
939      * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
940      * device} or be the notification assistant in order to use this method.
941      *
942      * @param pkg The package to retrieve channel groups for.
943      */
getNotificationChannelGroups(@onNull String pkg, @NonNull UserHandle user)944     public final List<NotificationChannelGroup> getNotificationChannelGroups(@NonNull String pkg,
945             @NonNull UserHandle user) {
946         if (!isBound()) return null;
947         try {
948 
949             return getNotificationInterface().getNotificationChannelGroupsFromPrivilegedListener(
950                     mWrapper, pkg, user).getList();
951         } catch (RemoteException e) {
952             Log.v(TAG, "Unable to contact notification manager", e);
953             throw e.rethrowFromSystemServer();
954         }
955     }
956 
957     /**
958      * Sets the notification trim that will be received via {@link #onNotificationPosted}.
959      *
960      * <p>
961      * Setting a trim other than {@link #TRIM_FULL} enables listeners that don't need access to the
962      * full notification features right away to reduce their memory footprint. Full notifications
963      * can be requested on-demand via {@link #getActiveNotifications(int)}.
964      *
965      * <p>
966      * Set to {@link #TRIM_FULL} initially.
967      *
968      * <p>The service should wait for the {@link #onListenerConnected()} event
969      * before performing this operation.
970      *
971      * @hide
972      * @removed
973      *
974      * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}.
975      *             See <code>TRIM_*</code> constants.
976      */
977     @SystemApi
setOnNotificationPostedTrim(int trim)978     public final void setOnNotificationPostedTrim(int trim) {
979         if (!isBound()) return;
980         try {
981             getNotificationInterface().setOnNotificationPostedTrimFromListener(mWrapper, trim);
982         } catch (RemoteException ex) {
983             Log.v(TAG, "Unable to contact notification manager", ex);
984         }
985     }
986 
987     /**
988      * Request the list of outstanding notifications (that is, those that are visible to the
989      * current user). Useful when you don't know what's already been posted.
990      *
991      * <p>The service should wait for the {@link #onListenerConnected()} event
992      * before performing this operation.
993      *
994      * @return An array of active notifications, sorted in natural order.
995      */
getActiveNotifications()996     public StatusBarNotification[] getActiveNotifications() {
997         StatusBarNotification[] activeNotifications = getActiveNotifications(null, TRIM_FULL);
998         return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
999     }
1000 
1001     /**
1002      * Like {@link #getActiveNotifications()}, but returns the list of currently snoozed
1003      * notifications, for all users this listener has access to.
1004      *
1005      * <p>The service should wait for the {@link #onListenerConnected()} event
1006      * before performing this operation.
1007      *
1008      * @return An array of snoozed notifications, sorted in natural order.
1009      */
getSnoozedNotifications()1010     public final StatusBarNotification[] getSnoozedNotifications() {
1011         try {
1012             ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
1013                     .getSnoozedNotificationsFromListener(mWrapper, TRIM_FULL);
1014             return cleanUpNotificationList(parceledList);
1015         } catch (android.os.RemoteException ex) {
1016             Log.v(TAG, "Unable to contact notification manager", ex);
1017         }
1018         return null;
1019     }
1020 
1021     /**
1022      * Request the list of outstanding notifications (that is, those that are visible to the
1023      * current user). Useful when you don't know what's already been posted.
1024      *
1025      * @hide
1026      * @removed
1027      *
1028      * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
1029      * @return An array of active notifications, sorted in natural order.
1030      */
1031     @SystemApi
getActiveNotifications(int trim)1032     public StatusBarNotification[] getActiveNotifications(int trim) {
1033         StatusBarNotification[] activeNotifications = getActiveNotifications(null, trim);
1034         return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
1035     }
1036 
1037     /**
1038      * Request one or more notifications by key. Useful if you have been keeping track of
1039      * notifications but didn't want to retain the bits, and now need to go back and extract
1040      * more data out of those notifications.
1041      *
1042      * <p>The service should wait for the {@link #onListenerConnected()} event
1043      * before performing this operation.
1044      *
1045      * @param keys the keys of the notifications to request
1046      * @return An array of notifications corresponding to the requested keys, in the
1047      * same order as the key list.
1048      */
getActiveNotifications(String[] keys)1049     public StatusBarNotification[] getActiveNotifications(String[] keys) {
1050         StatusBarNotification[] activeNotifications = getActiveNotifications(keys, TRIM_FULL);
1051         return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
1052     }
1053 
1054     /**
1055      * Request one or more notifications by key. Useful if you have been keeping track of
1056      * notifications but didn't want to retain the bits, and now need to go back and extract
1057      * more data out of those notifications.
1058      *
1059      * @hide
1060      * @removed
1061      *
1062      * @param keys the keys of the notifications to request
1063      * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
1064      * @return An array of notifications corresponding to the requested keys, in the
1065      * same order as the key list.
1066      */
1067     @SystemApi
getActiveNotifications(String[] keys, int trim)1068     public StatusBarNotification[] getActiveNotifications(String[] keys, int trim) {
1069         if (!isBound())
1070             return null;
1071         try {
1072             ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
1073                     .getActiveNotificationsFromListener(mWrapper, keys, trim);
1074             return cleanUpNotificationList(parceledList);
1075         } catch (android.os.RemoteException | BadParcelableException ex) {
1076             Log.v(TAG, "Unable to contact notification manager", ex);
1077         }
1078         return null;
1079     }
1080 
cleanUpNotificationList( ParceledListSlice<StatusBarNotification> parceledList)1081     private StatusBarNotification[] cleanUpNotificationList(
1082             ParceledListSlice<StatusBarNotification> parceledList) {
1083         if (parceledList == null || parceledList.getList() == null) {
1084             return new StatusBarNotification[0];
1085         }
1086         List<StatusBarNotification> list = parceledList.getList();
1087         ArrayList<StatusBarNotification> corruptNotifications = null;
1088         int N = list.size();
1089         for (int i = 0; i < N; i++) {
1090             StatusBarNotification sbn = list.get(i);
1091             Notification notification = sbn.getNotification();
1092             try {
1093                 // convert icon metadata to legacy format for older clients
1094                 createLegacyIconExtras(notification);
1095                 // populate remote views for older clients.
1096                 maybePopulateRemoteViews(notification);
1097                 // populate people for older clients.
1098                 maybePopulatePeople(notification);
1099             } catch (IllegalArgumentException e) {
1100                 if (corruptNotifications == null) {
1101                     corruptNotifications = new ArrayList<>(N);
1102                 }
1103                 corruptNotifications.add(sbn);
1104                 Log.w(TAG, "get(Active/Snoozed)Notifications: can't rebuild notification from " +
1105                         sbn.getPackageName());
1106             }
1107         }
1108         if (corruptNotifications != null) {
1109             list.removeAll(corruptNotifications);
1110         }
1111         return list.toArray(new StatusBarNotification[list.size()]);
1112     }
1113 
1114     /**
1115      * Gets the set of hints representing current state.
1116      *
1117      * <p>
1118      * The current state may differ from the requested state if the hint represents state
1119      * shared across all listeners or a feature the notification host does not support or refuses
1120      * to grant.
1121      *
1122      * <p>The service should wait for the {@link #onListenerConnected()} event
1123      * before performing this operation.
1124      *
1125      * @return Zero or more of the HINT_ constants.
1126      */
getCurrentListenerHints()1127     public final int getCurrentListenerHints() {
1128         if (!isBound()) return 0;
1129         try {
1130             return getNotificationInterface().getHintsFromListener(mWrapper);
1131         } catch (android.os.RemoteException ex) {
1132             Log.v(TAG, "Unable to contact notification manager", ex);
1133             return 0;
1134         }
1135     }
1136 
1137     /**
1138      * Gets the current notification interruption filter active on the host.
1139      *
1140      * <p>
1141      * The interruption filter defines which notifications are allowed to interrupt the user
1142      * (e.g. via sound &amp; vibration) and is applied globally. Listeners can find out whether
1143      * a specific notification matched the interruption filter via
1144      * {@link Ranking#matchesInterruptionFilter()}.
1145      * <p>
1146      * The current filter may differ from the previously requested filter if the notification host
1147      * does not support or refuses to apply the requested filter, or if another component changed
1148      * the filter in the meantime.
1149      * <p>
1150      * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
1151      *
1152      * <p>The service should wait for the {@link #onListenerConnected()} event
1153      * before performing this operation.
1154      *
1155      * @return One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when
1156      * unavailable.
1157      */
getCurrentInterruptionFilter()1158     public final int getCurrentInterruptionFilter() {
1159         if (!isBound()) return INTERRUPTION_FILTER_UNKNOWN;
1160         try {
1161             return getNotificationInterface().getInterruptionFilterFromListener(mWrapper);
1162         } catch (android.os.RemoteException ex) {
1163             Log.v(TAG, "Unable to contact notification manager", ex);
1164             return INTERRUPTION_FILTER_UNKNOWN;
1165         }
1166     }
1167 
1168     /**
1169      * Clears listener hints set via {@link #getCurrentListenerHints()}.
1170      *
1171      * <p>The service should wait for the {@link #onListenerConnected()} event
1172      * before performing this operation.
1173      */
clearRequestedListenerHints()1174     public final void clearRequestedListenerHints() {
1175         if (!isBound()) return;
1176         try {
1177             getNotificationInterface().clearRequestedListenerHints(mWrapper);
1178         } catch (android.os.RemoteException ex) {
1179             Log.v(TAG, "Unable to contact notification manager", ex);
1180         }
1181     }
1182 
1183     /**
1184      * Sets the desired {@link #getCurrentListenerHints() listener hints}.
1185      *
1186      * <p>
1187      * This is merely a request, the host may or may not choose to take action depending
1188      * on other listener requests or other global state.
1189      * <p>
1190      * Listen for updates using {@link #onListenerHintsChanged(int)}.
1191      *
1192      * <p>The service should wait for the {@link #onListenerConnected()} event
1193      * before performing this operation.
1194      *
1195      * @param hints One or more of the HINT_ constants.
1196      */
requestListenerHints(int hints)1197     public final void requestListenerHints(int hints) {
1198         if (!isBound()) return;
1199         try {
1200             getNotificationInterface().requestHintsFromListener(mWrapper, hints);
1201         } catch (android.os.RemoteException ex) {
1202             Log.v(TAG, "Unable to contact notification manager", ex);
1203         }
1204     }
1205 
1206     /**
1207      * Sets the desired {@link #getCurrentInterruptionFilter() interruption filter}.
1208      *
1209      * <p>
1210      * This is merely a request, the host may or may not choose to apply the requested
1211      * interruption filter depending on other listener requests or other global state.
1212      * <p>
1213      * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
1214      *
1215      * <p>Apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above (with some
1216      * exceptions, such as companion device managers) cannot modify the global interruption filter.
1217      * Calling this method will instead activate or deactivate an
1218      * {@link android.app.AutomaticZenRule} associated to the app.
1219      *
1220      * <p>The service should wait for the {@link #onListenerConnected()} event
1221      * before performing this operation.
1222      *
1223      * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants.
1224      */
requestInterruptionFilter(int interruptionFilter)1225     public final void requestInterruptionFilter(int interruptionFilter) {
1226         if (!isBound()) return;
1227         try {
1228             getNotificationInterface()
1229                     .requestInterruptionFilterFromListener(mWrapper, interruptionFilter);
1230         } catch (android.os.RemoteException ex) {
1231             Log.v(TAG, "Unable to contact notification manager", ex);
1232         }
1233     }
1234 
1235     /**
1236      * Returns current ranking information.
1237      *
1238      * <p>
1239      * The returned object represents the current ranking snapshot and only
1240      * applies for currently active notifications.
1241      * <p>
1242      * Generally you should use the RankingMap that is passed with events such
1243      * as {@link #onNotificationPosted(StatusBarNotification, RankingMap)},
1244      * {@link #onNotificationRemoved(StatusBarNotification, RankingMap)}, and
1245      * so on. This method should only be used when needing access outside of
1246      * such events, for example to retrieve the RankingMap right after
1247      * initialization.
1248      *
1249      * <p>The service should wait for the {@link #onListenerConnected()} event
1250      * before performing this operation.
1251      *
1252      * @return A {@link RankingMap} object providing access to ranking information
1253      */
getCurrentRanking()1254     public RankingMap getCurrentRanking() {
1255         synchronized (mLock) {
1256             return mRankingMap;
1257         }
1258     }
1259 
1260     /**
1261      * This is not the lifecycle event you are looking for.
1262      *
1263      * <p>The service should wait for the {@link #onListenerConnected()} event
1264      * before performing any operations.
1265      */
1266     @Override
onBind(Intent intent)1267     public IBinder onBind(Intent intent) {
1268         if (mWrapper == null) {
1269             mWrapper = new NotificationListenerWrapper();
1270         }
1271         return mWrapper;
1272     }
1273 
1274     /** @hide */
1275     @UnsupportedAppUsage
isBound()1276     protected boolean isBound() {
1277         if (mWrapper == null) {
1278             Log.w(TAG, "Notification listener service not yet bound.");
1279             return false;
1280         }
1281         return true;
1282     }
1283 
1284     @Override
onDestroy()1285     public void onDestroy() {
1286         onListenerDisconnected();
1287         super.onDestroy();
1288     }
1289 
1290     /**
1291      * Directly register this service with the Notification Manager.
1292      *
1293      * <p>Only system services may use this call. It will fail for non-system callers.
1294      * Apps should ask the user to add their listener in Settings.
1295      *
1296      * @param context Context required for accessing resources. Since this service isn't
1297      *    launched as a real Service when using this method, a context has to be passed in.
1298      * @param componentName the component that will consume the notification information
1299      * @param currentUser the user to use as the stream filter
1300      * @hide
1301      * @removed
1302      */
1303     @SystemApi
registerAsSystemService(Context context, ComponentName componentName, int currentUser)1304     public void registerAsSystemService(Context context, ComponentName componentName,
1305             int currentUser) throws RemoteException {
1306         if (mWrapper == null) {
1307             mWrapper = new NotificationListenerWrapper();
1308         }
1309         mSystemContext = context;
1310         INotificationManager noMan = getNotificationInterface();
1311         mHandler = new MyHandler(context.getMainLooper());
1312         mCurrentUser = currentUser;
1313         noMan.registerListener(mWrapper, componentName, currentUser);
1314     }
1315 
1316     /**
1317      * Directly unregister this service from the Notification Manager.
1318      *
1319      * <p>This method will fail for listeners that were not registered
1320      * with (@link registerAsService).
1321      * @hide
1322      * @removed
1323      */
1324     @SystemApi
unregisterAsSystemService()1325     public void unregisterAsSystemService() throws RemoteException {
1326         if (mWrapper != null) {
1327             INotificationManager noMan = getNotificationInterface();
1328             noMan.unregisterListener(mWrapper, mCurrentUser);
1329         }
1330     }
1331 
1332     /**
1333      * Request that the listener be rebound, after a previous call to {@link #requestUnbind}.
1334      *
1335      * <p>This method will fail for listeners that have
1336      * not been granted the permission by the user.
1337      */
requestRebind(ComponentName componentName)1338     public static void requestRebind(ComponentName componentName) {
1339         INotificationManager noMan = INotificationManager.Stub.asInterface(
1340                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
1341         try {
1342             noMan.requestBindListener(componentName);
1343         } catch (RemoteException ex) {
1344             throw ex.rethrowFromSystemServer();
1345         }
1346     }
1347 
1348     /**
1349      * Request that the service be unbound.
1350      *
1351      * <p>This method will fail for components that are not part of the calling app.
1352      */
requestUnbind(@onNull ComponentName componentName)1353     public static void requestUnbind(@NonNull ComponentName componentName) {
1354         INotificationManager noMan = INotificationManager.Stub.asInterface(
1355                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
1356         try {
1357             noMan.requestUnbindListenerComponent(componentName);
1358         } catch (RemoteException ex) {
1359             throw ex.rethrowFromSystemServer();
1360         }
1361     }
1362 
1363     /**
1364      * Request that the service be unbound.
1365      *
1366      * <p>Once this is called, you will no longer receive updates and no method calls are
1367      * guaranteed to be successful, until you next receive the {@link #onListenerConnected()} event.
1368      * The service will likely be killed by the system after this call.
1369      *
1370      * <p>The service should wait for the {@link #onListenerConnected()} event
1371      * before performing this operation. I know it's tempting, but you must wait.
1372      */
requestUnbind()1373     public final void requestUnbind() {
1374         if (mWrapper != null) {
1375             INotificationManager noMan = getNotificationInterface();
1376             try {
1377                 noMan.requestUnbindListener(mWrapper);
1378                 // Disable future messages.
1379                 isConnected = false;
1380             } catch (RemoteException ex) {
1381                 throw ex.rethrowFromSystemServer();
1382             }
1383         }
1384     }
1385 
1386     /**
1387      * Convert new-style Icons to legacy representations for pre-M clients.
1388      * @hide
1389      */
createLegacyIconExtras(Notification n)1390     public final void createLegacyIconExtras(Notification n) {
1391         if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) {
1392             Icon smallIcon = n.getSmallIcon();
1393             Icon largeIcon = n.getLargeIcon();
1394             if (smallIcon != null && smallIcon.getType() == Icon.TYPE_RESOURCE) {
1395                 n.extras.putInt(Notification.EXTRA_SMALL_ICON, smallIcon.getResId());
1396                 n.icon = smallIcon.getResId();
1397             }
1398             if (largeIcon != null) {
1399                 Drawable d = largeIcon.loadDrawable(getContext());
1400                 if (d != null && d instanceof BitmapDrawable) {
1401                     final Bitmap largeIconBits = ((BitmapDrawable) d).getBitmap();
1402                     n.extras.putParcelable(Notification.EXTRA_LARGE_ICON, largeIconBits);
1403                     n.largeIcon = largeIconBits;
1404                 }
1405             }
1406         }
1407     }
1408 
1409     /**
1410      * Populates remote views for pre-N targeting apps.
1411      */
maybePopulateRemoteViews(Notification notification)1412     private void maybePopulateRemoteViews(Notification notification) {
1413         if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
1414             Builder builder = Builder.recoverBuilder(getContext(), notification);
1415 
1416             // Some styles wrap Notification's contentView, bigContentView and headsUpContentView.
1417             // First inflate them all, only then set them to avoid recursive wrapping.
1418             RemoteViews content = builder.createContentView();
1419             RemoteViews big = builder.createBigContentView();
1420             RemoteViews headsUp = builder.createHeadsUpContentView();
1421 
1422             notification.contentView = content;
1423             notification.bigContentView = big;
1424             notification.headsUpContentView = headsUp;
1425         }
1426     }
1427 
1428     /**
1429      * Populates remote views for pre-P targeting apps.
1430      */
maybePopulatePeople(Notification notification)1431     private void maybePopulatePeople(Notification notification) {
1432         if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P) {
1433             ArrayList<Person> people = notification.extras.getParcelableArrayList(
1434                     Notification.EXTRA_PEOPLE_LIST, android.app.Person.class);
1435             if (people != null && !people.isEmpty()) {
1436                 int size = people.size();
1437                 String[] peopleArray = new String[size];
1438                 for (int i = 0; i < size; i++) {
1439                     Person person = people.get(i);
1440                     peopleArray[i] = person.resolveToLegacyUri();
1441                 }
1442                 notification.extras.putStringArray(Notification.EXTRA_PEOPLE, peopleArray);
1443             }
1444         }
1445     }
1446 
1447     /** @hide */
1448     protected class NotificationListenerWrapper extends INotificationListener.Stub {
1449         @Override
onNotificationPosted(IStatusBarNotificationHolder sbnHolder, NotificationRankingUpdate update)1450         public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
1451                 NotificationRankingUpdate update) {
1452             StatusBarNotification sbn;
1453             try {
1454                 sbn = sbnHolder.get();
1455             } catch (RemoteException e) {
1456                 Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);
1457                 return;
1458             }
1459             if (sbn == null) {
1460                 Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification");
1461                 return;
1462             }
1463 
1464             try {
1465                 // convert icon metadata to legacy format for older clients
1466                 createLegacyIconExtras(sbn.getNotification());
1467                 maybePopulateRemoteViews(sbn.getNotification());
1468                 maybePopulatePeople(sbn.getNotification());
1469             } catch (IllegalArgumentException e) {
1470                 // warn and drop corrupt notification
1471                 Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +
1472                         sbn.getPackageName());
1473                 sbn = null;
1474             }
1475 
1476             // protect subclass from concurrent modifications of (@link mNotificationKeys}.
1477             synchronized (mLock) {
1478                 applyUpdateLocked(update);
1479                 if (sbn != null) {
1480                     SomeArgs args = SomeArgs.obtain();
1481                     args.arg1 = sbn;
1482                     args.arg2 = mRankingMap;
1483                     mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,
1484                             args).sendToTarget();
1485                 } else {
1486                     // still pass along the ranking map, it may contain other information
1487                     mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
1488                             mRankingMap).sendToTarget();
1489                 }
1490             }
1491 
1492         }
1493 
1494         @Override
onNotificationRemoved(IStatusBarNotificationHolder sbnHolder, NotificationRankingUpdate update, NotificationStats stats, int reason)1495         public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,
1496                 NotificationRankingUpdate update, NotificationStats stats, int reason) {
1497             StatusBarNotification sbn;
1498             try {
1499                 sbn = sbnHolder.get();
1500             } catch (RemoteException e) {
1501                 Log.w(TAG, "onNotificationRemoved: Error receiving StatusBarNotification", e);
1502                 return;
1503             }
1504             if (sbn == null) {
1505                 Log.w(TAG, "onNotificationRemoved: Error receiving StatusBarNotification");
1506                 return;
1507             }
1508             // protect subclass from concurrent modifications of (@link mNotificationKeys}.
1509             synchronized (mLock) {
1510                 applyUpdateLocked(update);
1511                 SomeArgs args = SomeArgs.obtain();
1512                 args.arg1 = sbn;
1513                 args.arg2 = mRankingMap;
1514                 args.arg3 = reason;
1515                 args.arg4 = stats;
1516                 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED,
1517                         args).sendToTarget();
1518             }
1519 
1520         }
1521 
1522         @Override
onListenerConnected(NotificationRankingUpdate update)1523         public void onListenerConnected(NotificationRankingUpdate update) {
1524             // protect subclass from concurrent modifications of (@link mNotificationKeys}.
1525             synchronized (mLock) {
1526                 applyUpdateLocked(update);
1527             }
1528             isConnected = true;
1529             mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_CONNECTED).sendToTarget();
1530         }
1531 
1532         @Override
onNotificationRankingUpdate(NotificationRankingUpdate update)1533         public void onNotificationRankingUpdate(NotificationRankingUpdate update)
1534                 throws RemoteException {
1535             // protect subclass from concurrent modifications of (@link mNotificationKeys}.
1536             synchronized (mLock) {
1537                 applyUpdateLocked(update);
1538                 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
1539                         mRankingMap).sendToTarget();
1540             }
1541 
1542         }
1543 
1544         @Override
onListenerHintsChanged(int hints)1545         public void onListenerHintsChanged(int hints) throws RemoteException {
1546             mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_HINTS_CHANGED,
1547                     hints, 0).sendToTarget();
1548         }
1549 
1550         @Override
onInterruptionFilterChanged(int interruptionFilter)1551         public void onInterruptionFilterChanged(int interruptionFilter) throws RemoteException {
1552             mHandler.obtainMessage(MyHandler.MSG_ON_INTERRUPTION_FILTER_CHANGED,
1553                     interruptionFilter, 0).sendToTarget();
1554         }
1555 
1556         @Override
onNotificationEnqueuedWithChannel( IStatusBarNotificationHolder notificationHolder, NotificationChannel channel, NotificationRankingUpdate update)1557         public void onNotificationEnqueuedWithChannel(
1558                 IStatusBarNotificationHolder notificationHolder, NotificationChannel channel,
1559                 NotificationRankingUpdate update)
1560                 throws RemoteException {
1561             // no-op in the listener
1562         }
1563 
1564         @Override
onNotificationsSeen(List<String> keys)1565         public void onNotificationsSeen(List<String> keys)
1566                 throws RemoteException {
1567             // no-op in the listener
1568         }
1569 
1570         @Override
onPanelRevealed(int items)1571         public void onPanelRevealed(int items) throws RemoteException {
1572             // no-op in the listener
1573         }
1574 
1575         @Override
onPanelHidden()1576         public void onPanelHidden() throws RemoteException {
1577             // no-op in the listener
1578         }
1579 
1580         @Override
onNotificationVisibilityChanged( String key, boolean isVisible)1581         public void onNotificationVisibilityChanged(
1582                 String key, boolean isVisible) {
1583             // no-op in the listener
1584         }
1585 
1586         @Override
onNotificationSnoozedUntilContext( IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId)1587         public void onNotificationSnoozedUntilContext(
1588                 IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId)
1589                 throws RemoteException {
1590             // no-op in the listener
1591         }
1592 
1593         @Override
onNotificationExpansionChanged( String key, boolean isUserAction, boolean isExpanded)1594         public void onNotificationExpansionChanged(
1595                 String key, boolean isUserAction, boolean isExpanded) {
1596             // no-op in the listener
1597         }
1598 
1599         @Override
onNotificationDirectReply(String key)1600         public void onNotificationDirectReply(String key) {
1601             // no-op in the listener
1602         }
1603 
1604         @Override
onSuggestedReplySent(String key, CharSequence reply, int source)1605         public void onSuggestedReplySent(String key, CharSequence reply, int source) {
1606             // no-op in the listener
1607         }
1608 
1609         @Override
onActionClicked(String key, Notification.Action action, int source)1610         public void onActionClicked(String key, Notification.Action action, int source) {
1611             // no-op in the listener
1612         }
1613 
1614         @Override
onNotificationClicked(String key)1615         public void onNotificationClicked(String key) {
1616             // no-op in the listener
1617         }
1618 
1619         @Override
onAllowedAdjustmentsChanged()1620         public void onAllowedAdjustmentsChanged() {
1621             // no-op in the listener
1622         }
1623 
1624         @Override
onNotificationChannelModification(String pkgName, UserHandle user, NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType)1625         public void onNotificationChannelModification(String pkgName, UserHandle user,
1626                 NotificationChannel channel,
1627                 @ChannelOrGroupModificationTypes int modificationType) {
1628             SomeArgs args = SomeArgs.obtain();
1629             args.arg1 = pkgName;
1630             args.arg2 = user;
1631             args.arg3 = channel;
1632             args.arg4 = modificationType;
1633             mHandler.obtainMessage(
1634                     MyHandler.MSG_ON_NOTIFICATION_CHANNEL_MODIFIED, args).sendToTarget();
1635         }
1636 
1637         @Override
onNotificationChannelGroupModification(String pkgName, UserHandle user, NotificationChannelGroup group, @ChannelOrGroupModificationTypes int modificationType)1638         public void onNotificationChannelGroupModification(String pkgName, UserHandle user,
1639                 NotificationChannelGroup group,
1640                 @ChannelOrGroupModificationTypes int modificationType) {
1641             SomeArgs args = SomeArgs.obtain();
1642             args.arg1 = pkgName;
1643             args.arg2 = user;
1644             args.arg3 = group;
1645             args.arg4 = modificationType;
1646             mHandler.obtainMessage(
1647                     MyHandler.MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED, args).sendToTarget();
1648         }
1649 
1650         @Override
onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons)1651         public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
1652             mHandler.obtainMessage(MyHandler.MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED,
1653                     hideSilentStatusIcons).sendToTarget();
1654         }
1655 
1656         @Override
onNotificationFeedbackReceived(String key, NotificationRankingUpdate update, Bundle feedback)1657         public void onNotificationFeedbackReceived(String key, NotificationRankingUpdate update,
1658                 Bundle feedback) {
1659             // no-op in the listener
1660         }
1661 
1662 
1663     }
1664 
1665     /**
1666      * @hide
1667      */
1668     @GuardedBy("mLock")
applyUpdateLocked(NotificationRankingUpdate update)1669     public final void applyUpdateLocked(NotificationRankingUpdate update) {
1670         mRankingMap = update.getRankingMap();
1671     }
1672 
1673     /** @hide */
getContext()1674     protected Context getContext() {
1675         if (mSystemContext != null) {
1676             return mSystemContext;
1677         }
1678         return this;
1679     }
1680 
1681     /**
1682      * Stores ranking related information on a currently active notification.
1683      *
1684      * <p>
1685      * Ranking objects aren't automatically updated as notification events
1686      * occur. Instead, ranking information has to be retrieved again via the
1687      * current {@link RankingMap}.
1688      */
1689     public static class Ranking {
1690 
1691         /**
1692          * Value signifying that the user and device policy manager have not expressed a lockscreen
1693          * visibility override for a notification.
1694          */
1695         public static final int VISIBILITY_NO_OVERRIDE = NotificationManager.VISIBILITY_NO_OVERRIDE;
1696 
1697         /**
1698          * The user is likely to have a negative reaction to this notification.
1699          */
1700         public static final int USER_SENTIMENT_NEGATIVE = -1;
1701         /**
1702          * It is not known how the user will react to this notification.
1703          */
1704         public static final int USER_SENTIMENT_NEUTRAL = 0;
1705         /**
1706          * The user is likely to have a positive reaction to this notification.
1707          */
1708         public static final int USER_SENTIMENT_POSITIVE = 1;
1709 
1710        /** @hide */
1711         @IntDef(prefix = { "USER_SENTIMENT_" }, value = {
1712                 USER_SENTIMENT_NEGATIVE, USER_SENTIMENT_NEUTRAL, USER_SENTIMENT_POSITIVE
1713         })
1714         @Retention(RetentionPolicy.SOURCE)
1715         public @interface UserSentiment {}
1716 
1717         /**
1718          * Notification was demoted in shade
1719          * @hide
1720          */
1721         public static final int RANKING_DEMOTED = -1;
1722         /**
1723          * Notification was unchanged
1724          * @hide
1725          */
1726         public static final int RANKING_UNCHANGED = 0;
1727         /**
1728          * Notification was promoted in shade
1729          * @hide
1730          */
1731         public static final int RANKING_PROMOTED = 1;
1732 
1733         /** @hide */
1734         @IntDef(prefix = { "RANKING_" }, value = {
1735                 RANKING_PROMOTED, RANKING_DEMOTED, RANKING_UNCHANGED
1736         })
1737         @Retention(RetentionPolicy.SOURCE)
1738         public @interface RankingAdjustment {}
1739 
1740         private @NonNull String mKey;
1741         private int mRank = -1;
1742         private boolean mIsAmbient;
1743         private boolean mMatchesInterruptionFilter;
1744         private int mVisibilityOverride;
1745         private int mSuppressedVisualEffects;
1746         private @NotificationManager.Importance int mImportance;
1747         private CharSequence mImportanceExplanation;
1748         private float mRankingScore;
1749         // System specified group key.
1750         private String mOverrideGroupKey;
1751         // Notification assistant channel override.
1752         private NotificationChannel mChannel;
1753         // Notification assistant people override.
1754         private ArrayList<String> mOverridePeople;
1755         // Notification assistant snooze criteria.
1756         private ArrayList<SnoozeCriterion> mSnoozeCriteria;
1757         private boolean mShowBadge;
1758         private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
1759         private boolean mHidden;
1760         private long mLastAudiblyAlertedMs;
1761         private boolean mNoisy;
1762         private ArrayList<Notification.Action> mSmartActions;
1763         private ArrayList<CharSequence> mSmartReplies;
1764         private boolean mCanBubble;
1765         private boolean mIsTextChanged;
1766         private boolean mIsConversation;
1767         private ShortcutInfo mShortcutInfo;
1768         private @RankingAdjustment int mRankingAdjustment;
1769         private boolean mIsBubble;
1770         // Notification assistant importance suggestion
1771         private int mProposedImportance;
1772         // Sensitive info detected by the notification assistant
1773         private boolean mSensitiveContent;
1774 
1775         private static final int PARCEL_VERSION = 2;
1776 
Ranking()1777         public Ranking() {
1778         }
1779 
1780         // You can parcel it, but it's not Parcelable
1781         /** @hide */
1782         @VisibleForTesting
writeToParcel(Parcel out, int flags)1783         public void writeToParcel(Parcel out, int flags) {
1784             final long start = out.dataPosition();
1785             out.writeInt(PARCEL_VERSION);
1786             out.writeString(mKey);
1787             out.writeInt(mRank);
1788             out.writeBoolean(mIsAmbient);
1789             out.writeBoolean(mMatchesInterruptionFilter);
1790             out.writeInt(mVisibilityOverride);
1791             out.writeInt(mSuppressedVisualEffects);
1792             out.writeInt(mImportance);
1793             out.writeCharSequence(mImportanceExplanation);
1794             out.writeFloat(mRankingScore);
1795             out.writeString(mOverrideGroupKey);
1796             out.writeParcelable(mChannel, flags);
1797             out.writeStringList(mOverridePeople);
1798             out.writeTypedList(mSnoozeCriteria, flags);
1799             out.writeBoolean(mShowBadge);
1800             out.writeInt(mUserSentiment);
1801             out.writeBoolean(mHidden);
1802             out.writeLong(mLastAudiblyAlertedMs);
1803             out.writeBoolean(mNoisy);
1804             out.writeTypedList(mSmartActions, flags);
1805             out.writeCharSequenceList(mSmartReplies);
1806             out.writeBoolean(mCanBubble);
1807             out.writeBoolean(mIsTextChanged);
1808             out.writeBoolean(mIsConversation);
1809             out.writeParcelable(mShortcutInfo, flags);
1810             out.writeInt(mRankingAdjustment);
1811             out.writeBoolean(mIsBubble);
1812             out.writeInt(mProposedImportance);
1813             out.writeBoolean(mSensitiveContent);
1814         }
1815 
1816         /** @hide */
1817         @VisibleForTesting
Ranking(Parcel in)1818         public Ranking(Parcel in) {
1819             final ClassLoader cl = getClass().getClassLoader();
1820 
1821             final int version = in.readInt();
1822             if (version != PARCEL_VERSION) {
1823                 throw new IllegalArgumentException("malformed Ranking parcel: " + in + " version "
1824                         + version + ", expected " + PARCEL_VERSION);
1825             }
1826             mKey = in.readString();
1827             mRank = in.readInt();
1828             mIsAmbient = in.readBoolean();
1829             mMatchesInterruptionFilter = in.readBoolean();
1830             mVisibilityOverride = in.readInt();
1831             mSuppressedVisualEffects = in.readInt();
1832             mImportance = in.readInt();
1833             mImportanceExplanation = in.readCharSequence(); // may be null
1834             mRankingScore = in.readFloat();
1835             mOverrideGroupKey = in.readString(); // may be null
1836             mChannel = in.readParcelable(cl, android.app.NotificationChannel.class); // may be null
1837             mOverridePeople = in.createStringArrayList();
1838             mSnoozeCriteria = in.createTypedArrayList(SnoozeCriterion.CREATOR);
1839             mShowBadge = in.readBoolean();
1840             mUserSentiment = in.readInt();
1841             mHidden = in.readBoolean();
1842             mLastAudiblyAlertedMs = in.readLong();
1843             mNoisy = in.readBoolean();
1844             mSmartActions = in.createTypedArrayList(Notification.Action.CREATOR);
1845             mSmartReplies = in.readCharSequenceList();
1846             mCanBubble = in.readBoolean();
1847             mIsTextChanged = in.readBoolean();
1848             mIsConversation = in.readBoolean();
1849             mShortcutInfo = in.readParcelable(cl, android.content.pm.ShortcutInfo.class);
1850             mRankingAdjustment = in.readInt();
1851             mIsBubble = in.readBoolean();
1852             mProposedImportance = in.readInt();
1853             mSensitiveContent = in.readBoolean();
1854         }
1855 
1856 
1857         /**
1858          * Returns the key of the notification this Ranking applies to.
1859          */
getKey()1860         public String getKey() {
1861             return mKey;
1862         }
1863 
1864         /**
1865          * Returns the rank of the notification.
1866          *
1867          * @return the rank of the notification, that is the 0-based index in
1868          *     the list of active notifications.
1869          */
getRank()1870         public int getRank() {
1871             return mRank;
1872         }
1873 
1874         /**
1875          * Returns whether the notification is an ambient notification, that is
1876          * a notification that doesn't require the user's immediate attention.
1877          */
isAmbient()1878         public boolean isAmbient() {
1879             return mIsAmbient;
1880         }
1881 
1882         /**
1883          * Returns the user or device policy manager specified visibility (see
1884          * {@link Notification#VISIBILITY_PRIVATE}, {@link Notification#VISIBILITY_PUBLIC},
1885          * {@link Notification#VISIBILITY_SECRET}) for this notification, or
1886          * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if
1887          * no such preference has been expressed.
1888          */
1889         public @Notification.NotificationVisibilityOverride
getLockscreenVisibilityOverride()1890         int getLockscreenVisibilityOverride() {
1891             return mVisibilityOverride;
1892         }
1893 
1894         /**
1895          * Returns the type(s) of visual effects that should be suppressed for this notification.
1896          * See {@link NotificationManager.Policy}, e.g.
1897          * {@link NotificationManager.Policy#SUPPRESSED_EFFECT_LIGHTS}.
1898          */
getSuppressedVisualEffects()1899         public int getSuppressedVisualEffects() {
1900             return mSuppressedVisualEffects;
1901         }
1902 
1903         /**
1904          * Returns whether the notification matches the user's interruption
1905          * filter.
1906          *
1907          * @return {@code true} if the notification is allowed by the filter, or
1908          * {@code false} if it is blocked.
1909          */
matchesInterruptionFilter()1910         public boolean matchesInterruptionFilter() {
1911             return mMatchesInterruptionFilter;
1912         }
1913 
1914         /**
1915          * Returns the importance of the notification, which dictates its
1916          * modes of presentation, see: {@link NotificationManager#IMPORTANCE_DEFAULT}, etc.
1917          *
1918          * @return the importance of the notification
1919          */
getImportance()1920         public @NotificationManager.Importance int getImportance() {
1921             return mImportance;
1922         }
1923 
1924         /**
1925          * If the importance has been overridden by user preference, then this will be non-null,
1926          * and should be displayed to the user.
1927          *
1928          * @return the explanation for the importance, or null if it is the natural importance
1929          */
getImportanceExplanation()1930         public CharSequence getImportanceExplanation() {
1931             return mImportanceExplanation;
1932         }
1933 
1934         /**
1935          * Returns the ranking score provided by the {@link NotificationAssistantService} to
1936          * sort the notifications in the shade
1937          *
1938          * @return the ranking score of the notification, range from -1 to 1
1939          * @hide
1940          */
getRankingScore()1941         public float getRankingScore() {
1942             return mRankingScore;
1943         }
1944 
1945         /**
1946          * Returns the proposed importance provided by the {@link NotificationAssistantService}.
1947          *
1948          * This can be used to suggest that the user change the importance of this type of
1949          * notification moving forward. A value of
1950          * {@link NotificationManager#IMPORTANCE_UNSPECIFIED} means that the NAS has not recommended
1951          * a change to the importance, and no UI should be shown to the user. See
1952          * {@link Adjustment#KEY_IMPORTANCE_PROPOSAL}.
1953          *
1954          * @return the importance of the notification
1955          * @hide
1956          */
1957         @SystemApi
getProposedImportance()1958         public @NotificationManager.Importance int getProposedImportance() {
1959             return mProposedImportance;
1960         }
1961 
1962         /**
1963          * Returns true if the notification text is sensitive (e.g. containing an OTP).
1964          *
1965          * @return whether the notification contains sensitive content
1966          * @hide
1967          */
1968         @SystemApi
hasSensitiveContent()1969         public boolean hasSensitiveContent() {
1970             return mSensitiveContent;
1971         }
1972 
1973         /**
1974          * If the system has overridden the group key, then this will be non-null, and this
1975          * key should be used to bundle notifications.
1976          */
getOverrideGroupKey()1977         public String getOverrideGroupKey() {
1978             return mOverrideGroupKey;
1979         }
1980 
1981         /**
1982          * Returns the notification channel this notification was posted to, which dictates
1983          * notification behavior and presentation.
1984          */
getChannel()1985         public NotificationChannel getChannel() {
1986             return mChannel;
1987         }
1988 
1989         /**
1990          * Returns how the system thinks the user feels about notifications from the
1991          * channel provided by {@link #getChannel()}. You can use this information to expose
1992          * controls to help the user block this channel's notifications, if the sentiment is
1993          * {@link #USER_SENTIMENT_NEGATIVE}, or emphasize this notification if the sentiment is
1994          * {@link #USER_SENTIMENT_POSITIVE}.
1995          */
getUserSentiment()1996         public int getUserSentiment() {
1997             return mUserSentiment;
1998         }
1999 
2000         /**
2001          * If the {@link NotificationAssistantService} has added people to this notification, then
2002          * this will be non-null.
2003          * @hide
2004          * @removed
2005          */
2006         @SystemApi
getAdditionalPeople()2007         public List<String> getAdditionalPeople() {
2008             return mOverridePeople;
2009         }
2010 
2011         /**
2012          * Returns snooze criteria provided by the {@link NotificationAssistantService}. If your
2013          * user interface displays options for snoozing notifications these criteria should be
2014          * displayed as well.
2015          * @hide
2016          * @removed
2017          */
2018         @SystemApi
getSnoozeCriteria()2019         public List<SnoozeCriterion> getSnoozeCriteria() {
2020             return mSnoozeCriteria;
2021         }
2022 
2023         /**
2024          * Returns a list of smart {@link Notification.Action} that can be added by the
2025          * notification assistant.
2026          */
getSmartActions()2027         public @NonNull List<Notification.Action> getSmartActions() {
2028             return mSmartActions == null ? Collections.emptyList() : mSmartActions;
2029         }
2030 
2031 
2032         /**
2033          * Sets the smart {@link Notification.Action} objects.
2034          *
2035          * Should ONLY be used in cases where smartActions need to be removed from, then restored
2036          * on, Ranking objects during Parceling, when they are transmitted between processes via
2037          * Shared Memory.
2038          *
2039          * @hide
2040          */
setSmartActions(@ullable ArrayList<Notification.Action> smartActions)2041         public void setSmartActions(@Nullable ArrayList<Notification.Action> smartActions) {
2042             mSmartActions = smartActions;
2043         }
2044 
2045         /**
2046          * Returns a list of smart replies that can be added by the notification assistant.
2047          */
getSmartReplies()2048         public @NonNull List<CharSequence> getSmartReplies() {
2049             return mSmartReplies == null ? Collections.emptyList() : mSmartReplies;
2050         }
2051 
2052         /**
2053          * Returns whether this notification can be displayed as a badge.
2054          *
2055          * @return true if the notification can be displayed as a badge, false otherwise.
2056          */
canShowBadge()2057         public boolean canShowBadge() {
2058             return mShowBadge;
2059         }
2060 
2061         /**
2062          * Returns whether the app that posted this notification is suspended, so this notification
2063          * should be hidden.
2064          *
2065          * @return true if the notification should be hidden, false otherwise.
2066          */
isSuspended()2067         public boolean isSuspended() {
2068             return mHidden;
2069         }
2070 
2071         /**
2072          * Returns the last time this notification alerted the user via sound or vibration.
2073          *
2074          * @return the time of the last alerting behavior, in milliseconds.
2075          */
2076         @CurrentTimeMillisLong
getLastAudiblyAlertedMillis()2077         public long getLastAudiblyAlertedMillis() {
2078             return mLastAudiblyAlertedMs;
2079         }
2080 
2081         /**
2082          * Returns whether the user has allowed bubbles globally, at the app level, and at the
2083          * channel level for this notification.
2084          *
2085          * <p>This does not take into account the current importance of the notification, the
2086          * current DND state, or whether the posting app is foreground.</p>
2087          */
canBubble()2088         public boolean canBubble() {
2089             return mCanBubble;
2090         }
2091 
2092         /** @hide */
isTextChanged()2093         public boolean isTextChanged() {
2094             return mIsTextChanged;
2095         }
2096 
2097         /** @hide */
isNoisy()2098         public boolean isNoisy() {
2099             return mNoisy;
2100         }
2101 
2102         /**
2103          * Returns whether this notification is a conversation notification, and would appear
2104          * in the conversation section of the notification shade, on devices that separate that
2105          * type of notification.
2106          */
isConversation()2107         public boolean isConversation() {
2108             return mIsConversation;
2109         }
2110 
2111         /**
2112          * Returns whether this notification is actively a bubble.
2113          * @hide
2114          */
isBubble()2115         public boolean isBubble() {
2116             return mIsBubble;
2117         }
2118 
2119         /**
2120          * Returns the shortcut information associated with this notification, if it is a
2121          * {@link #isConversation() conversation notification}.
2122          * <p>This might be null even if the notification is a conversation notification, if
2123          * the posting app hasn't opted into the full conversation feature set yet.</p>
2124          */
getConversationShortcutInfo()2125         public @Nullable ShortcutInfo getConversationShortcutInfo() {
2126             return mShortcutInfo;
2127         }
2128 
2129         /**
2130          * Returns the intended transition to ranking passed by {@link NotificationAssistantService}
2131          * @hide
2132          */
getRankingAdjustment()2133         public @RankingAdjustment int getRankingAdjustment() {
2134             return mRankingAdjustment;
2135         }
2136 
2137         /**
2138          * @hide
2139          */
2140         @VisibleForTesting
populate(String key, int rank, boolean matchesInterruptionFilter, int visibilityOverride, int suppressedVisualEffects, int importance, CharSequence explanation, String overrideGroupKey, NotificationChannel channel, ArrayList<String> overridePeople, ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge, int userSentiment, boolean hidden, long lastAudiblyAlertedMs, boolean noisy, ArrayList<Notification.Action> smartActions, ArrayList<CharSequence> smartReplies, boolean canBubble, boolean isTextChanged, boolean isConversation, ShortcutInfo shortcutInfo, int rankingAdjustment, boolean isBubble, int proposedImportance, boolean sensitiveContent)2141         public void populate(String key, int rank, boolean matchesInterruptionFilter,
2142                 int visibilityOverride, int suppressedVisualEffects, int importance,
2143                 CharSequence explanation, String overrideGroupKey,
2144                 NotificationChannel channel, ArrayList<String> overridePeople,
2145                 ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
2146                 int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
2147                 boolean noisy, ArrayList<Notification.Action> smartActions,
2148                 ArrayList<CharSequence> smartReplies, boolean canBubble,
2149                 boolean isTextChanged, boolean isConversation, ShortcutInfo shortcutInfo,
2150                 int rankingAdjustment, boolean isBubble, int proposedImportance,
2151                 boolean sensitiveContent) {
2152             mKey = key;
2153             mRank = rank;
2154             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
2155             mMatchesInterruptionFilter = matchesInterruptionFilter;
2156             mVisibilityOverride = visibilityOverride;
2157             mSuppressedVisualEffects = suppressedVisualEffects;
2158             mImportance = importance;
2159             mImportanceExplanation = explanation;
2160             mOverrideGroupKey = overrideGroupKey;
2161             mChannel = channel;
2162             mOverridePeople = overridePeople;
2163             mSnoozeCriteria = snoozeCriteria;
2164             mShowBadge = showBadge;
2165             mUserSentiment = userSentiment;
2166             mHidden = hidden;
2167             mLastAudiblyAlertedMs = lastAudiblyAlertedMs;
2168             mNoisy = noisy;
2169             mSmartActions = smartActions;
2170             mSmartReplies = smartReplies;
2171             mCanBubble = canBubble;
2172             mIsTextChanged = isTextChanged;
2173             mIsConversation = isConversation;
2174             mShortcutInfo = shortcutInfo;
2175             mRankingAdjustment = rankingAdjustment;
2176             mIsBubble = isBubble;
2177             mProposedImportance = proposedImportance;
2178             mSensitiveContent = sensitiveContent;
2179         }
2180 
2181         /**
2182          * @hide
2183          */
2184         public @NonNull Ranking withAudiblyAlertedInfo(@Nullable Ranking previous) {
2185             if (previous != null && previous.mLastAudiblyAlertedMs > 0
2186                     && this.mLastAudiblyAlertedMs <= 0) {
2187                 this.mLastAudiblyAlertedMs = previous.mLastAudiblyAlertedMs;
2188             }
2189             return this;
2190         }
2191 
2192         /**
2193          * @hide
2194          */
populate(Ranking other)2195         public void populate(Ranking other) {
2196             populate(other.mKey,
2197                     other.mRank,
2198                     other.mMatchesInterruptionFilter,
2199                     other.mVisibilityOverride,
2200                     other.mSuppressedVisualEffects,
2201                     other.mImportance,
2202                     other.mImportanceExplanation,
2203                     other.mOverrideGroupKey,
2204                     other.mChannel,
2205                     other.mOverridePeople,
2206                     other.mSnoozeCriteria,
2207                     other.mShowBadge,
2208                     other.mUserSentiment,
2209                     other.mHidden,
2210                     other.mLastAudiblyAlertedMs,
2211                     other.mNoisy,
2212                     other.mSmartActions,
2213                     other.mSmartReplies,
2214                     other.mCanBubble,
2215                     other.mIsTextChanged,
2216                     other.mIsConversation,
2217                     other.mShortcutInfo,
2218                     other.mRankingAdjustment,
2219                     other.mIsBubble,
2220                     other.mProposedImportance,
2221                     other.mSensitiveContent);
2222         }
2223 
2224         /**
2225          * {@hide}
2226          */
importanceToString(int importance)2227         public static String importanceToString(int importance) {
2228             switch (importance) {
2229                 case NotificationManager.IMPORTANCE_UNSPECIFIED:
2230                     return "UNSPECIFIED";
2231                 case NotificationManager.IMPORTANCE_NONE:
2232                     return "NONE";
2233                 case NotificationManager.IMPORTANCE_MIN:
2234                     return "MIN";
2235                 case NotificationManager.IMPORTANCE_LOW:
2236                     return "LOW";
2237                 case NotificationManager.IMPORTANCE_DEFAULT:
2238                     return "DEFAULT";
2239                 case NotificationManager.IMPORTANCE_HIGH:
2240                 case NotificationManager.IMPORTANCE_MAX:
2241                     return "HIGH";
2242                 default:
2243                     return "UNKNOWN(" + String.valueOf(importance) + ")";
2244             }
2245         }
2246 
2247         @Override
equals(@ullable Object o)2248         public boolean equals(@Nullable Object o) {
2249             if (this == o) return true;
2250             if (o == null || getClass() != o.getClass()) return false;
2251 
2252             Ranking other = (Ranking) o;
2253             return Objects.equals(mKey, other.mKey)
2254                     && Objects.equals(mRank, other.mRank)
2255                     && Objects.equals(mMatchesInterruptionFilter, other.mMatchesInterruptionFilter)
2256                     && Objects.equals(mVisibilityOverride, other.mVisibilityOverride)
2257                     && Objects.equals(mSuppressedVisualEffects, other.mSuppressedVisualEffects)
2258                     && Objects.equals(mImportance, other.mImportance)
2259                     && Objects.equals(mImportanceExplanation, other.mImportanceExplanation)
2260                     && Objects.equals(mOverrideGroupKey, other.mOverrideGroupKey)
2261                     && Objects.equals(mChannel, other.mChannel)
2262                     && Objects.equals(mOverridePeople, other.mOverridePeople)
2263                     && Objects.equals(mSnoozeCriteria, other.mSnoozeCriteria)
2264                     && Objects.equals(mShowBadge, other.mShowBadge)
2265                     && Objects.equals(mUserSentiment, other.mUserSentiment)
2266                     && Objects.equals(mHidden, other.mHidden)
2267                     && Objects.equals(mLastAudiblyAlertedMs, other.mLastAudiblyAlertedMs)
2268                     && Objects.equals(mNoisy, other.mNoisy)
2269                     // Action.equals() doesn't exist so let's just compare list lengths
2270                     && ((mSmartActions == null ? 0 : mSmartActions.size())
2271                         == (other.mSmartActions == null ? 0 : other.mSmartActions.size()))
2272                     && Objects.equals(mSmartReplies, other.mSmartReplies)
2273                     && Objects.equals(mCanBubble, other.mCanBubble)
2274                     && Objects.equals(mIsTextChanged, other.mIsTextChanged)
2275                     && Objects.equals(mIsConversation, other.mIsConversation)
2276                     // Shortcutinfo doesn't have equals either; use id
2277                     &&  Objects.equals((mShortcutInfo == null ? 0 : mShortcutInfo.getId()),
2278                     (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId()))
2279                     && Objects.equals(mRankingAdjustment, other.mRankingAdjustment)
2280                     && Objects.equals(mIsBubble, other.mIsBubble)
2281                     && Objects.equals(mProposedImportance, other.mProposedImportance)
2282                     && Objects.equals(mSensitiveContent, other.mSensitiveContent);
2283         }
2284     }
2285 
2286     /**
2287      * Provides access to ranking information on currently active
2288      * notifications.
2289      *
2290      * <p>
2291      * Note that this object represents a ranking snapshot that only applies to
2292      * notifications active at the time of retrieval.
2293      */
2294     public static class RankingMap implements Parcelable {
2295         private ArrayList<String> mOrderedKeys = new ArrayList<>();
2296         // Note: all String keys should be intern'd as pointers into mOrderedKeys
2297         private ArrayMap<String, Ranking> mRankings = new ArrayMap<>();
2298 
2299         /**
2300          * @hide
2301          */
RankingMap(Ranking[] rankings)2302         public RankingMap(Ranking[] rankings) {
2303             for (int i = 0; i < rankings.length; i++) {
2304                 final String key = rankings[i].getKey();
2305                 mOrderedKeys.add(key);
2306                 mRankings.put(key, rankings[i]);
2307             }
2308         }
2309 
2310         // -- parcelable interface --
2311 
RankingMap(Parcel in)2312         private RankingMap(Parcel in) {
2313             final ClassLoader cl = getClass().getClassLoader();
2314             final int count = in.readInt();
2315             mOrderedKeys.ensureCapacity(count);
2316             mRankings.ensureCapacity(count);
2317             for (int i = 0; i < count; i++) {
2318                 final Ranking r = new Ranking(in);
2319                 final String key = r.getKey();
2320                 mOrderedKeys.add(key);
2321                 mRankings.put(key, r);
2322             }
2323         }
2324 
2325         @Override
equals(@ullable Object o)2326         public boolean equals(@Nullable Object o) {
2327             if (this == o) return true;
2328             if (o == null || getClass() != o.getClass()) return false;
2329 
2330             RankingMap other = (RankingMap) o;
2331 
2332             return mOrderedKeys.equals(other.mOrderedKeys)
2333                     && mRankings.equals(other.mRankings);
2334 
2335         }
2336 
2337         @Override
describeContents()2338         public int describeContents() {
2339             return 0;
2340         }
2341 
2342         @Override
writeToParcel(Parcel out, int flags)2343         public void writeToParcel(Parcel out, int flags) {
2344             final int count = mOrderedKeys.size();
2345             out.writeInt(count);
2346             for (int i = 0; i < count; i++) {
2347                 mRankings.get(mOrderedKeys.get(i)).writeToParcel(out, flags);
2348             }
2349         }
2350 
2351         public static final @android.annotation.NonNull Creator<RankingMap> CREATOR = new Creator<RankingMap>() {
2352             @Override
2353             public RankingMap createFromParcel(Parcel source) {
2354                 return new RankingMap(source);
2355             }
2356 
2357             @Override
2358             public RankingMap[] newArray(int size) {
2359                 return new RankingMap[size];
2360             }
2361         };
2362 
2363         /**
2364          * Request the list of notification keys in their current ranking
2365          * order.
2366          *
2367          * @return An array of active notification keys, in their ranking order.
2368          */
getOrderedKeys()2369         public String[] getOrderedKeys() {
2370             return mOrderedKeys.toArray(new String[0]);
2371         }
2372 
2373         /**
2374          * Populates outRanking with ranking information for the notification
2375          * with the given key.
2376          *
2377          * @return true if a valid key has been passed and outRanking has
2378          * been populated; false otherwise
2379          */
getRanking(String key, Ranking outRanking)2380         public boolean getRanking(String key, Ranking outRanking) {
2381             if (mRankings.containsKey(key)) {
2382                 outRanking.populate(mRankings.get(key));
2383                 return true;
2384             }
2385             return false;
2386         }
2387 
2388         /**
2389          * Get a reference to the actual Ranking object corresponding to the key.
2390          *
2391          * @hide
2392          */
getRawRankingObject(String key)2393         public Ranking getRawRankingObject(String key) {
2394             return mRankings.get(key);
2395         }
2396     }
2397 
2398     private final class MyHandler extends Handler {
2399         public static final int MSG_ON_NOTIFICATION_POSTED = 1;
2400         public static final int MSG_ON_NOTIFICATION_REMOVED = 2;
2401         public static final int MSG_ON_LISTENER_CONNECTED = 3;
2402         public static final int MSG_ON_NOTIFICATION_RANKING_UPDATE = 4;
2403         public static final int MSG_ON_LISTENER_HINTS_CHANGED = 5;
2404         public static final int MSG_ON_INTERRUPTION_FILTER_CHANGED = 6;
2405         public static final int MSG_ON_NOTIFICATION_CHANNEL_MODIFIED = 7;
2406         public static final int MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED = 8;
2407         public static final int MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED = 9;
2408 
MyHandler(Looper looper)2409         public MyHandler(Looper looper) {
2410             super(looper, null, false);
2411         }
2412 
2413         @Override
handleMessage(Message msg)2414         public void handleMessage(Message msg) {
2415             if (!isConnected) {
2416                 return;
2417             }
2418             switch (msg.what) {
2419                 case MSG_ON_NOTIFICATION_POSTED: {
2420                     SomeArgs args = (SomeArgs) msg.obj;
2421                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
2422                     RankingMap rankingMap = (RankingMap) args.arg2;
2423                     args.recycle();
2424                     onNotificationPosted(sbn, rankingMap);
2425                 } break;
2426 
2427                 case MSG_ON_NOTIFICATION_REMOVED: {
2428                     SomeArgs args = (SomeArgs) msg.obj;
2429                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
2430                     RankingMap rankingMap = (RankingMap) args.arg2;
2431                     int reason = (int) args.arg3;
2432                     NotificationStats stats = (NotificationStats) args.arg4;
2433                     args.recycle();
2434                     onNotificationRemoved(sbn, rankingMap, stats, reason);
2435                 } break;
2436 
2437                 case MSG_ON_LISTENER_CONNECTED: {
2438                     onListenerConnected();
2439                 } break;
2440 
2441                 case MSG_ON_NOTIFICATION_RANKING_UPDATE: {
2442                     RankingMap rankingMap = (RankingMap) msg.obj;
2443                     onNotificationRankingUpdate(rankingMap);
2444                 } break;
2445 
2446                 case MSG_ON_LISTENER_HINTS_CHANGED: {
2447                     final int hints = msg.arg1;
2448                     onListenerHintsChanged(hints);
2449                 } break;
2450 
2451                 case MSG_ON_INTERRUPTION_FILTER_CHANGED: {
2452                     final int interruptionFilter = msg.arg1;
2453                     onInterruptionFilterChanged(interruptionFilter);
2454                 } break;
2455 
2456                 case MSG_ON_NOTIFICATION_CHANNEL_MODIFIED: {
2457                     SomeArgs args = (SomeArgs) msg.obj;
2458                     String pkgName = (String) args.arg1;
2459                     UserHandle user= (UserHandle) args.arg2;
2460                     NotificationChannel channel = (NotificationChannel) args.arg3;
2461                     int modificationType = (int) args.arg4;
2462                     args.recycle();
2463                     onNotificationChannelModified(pkgName, user, channel, modificationType);
2464                 } break;
2465 
2466                 case MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED: {
2467                     SomeArgs args = (SomeArgs) msg.obj;
2468                     String pkgName = (String) args.arg1;
2469                     UserHandle user = (UserHandle) args.arg2;
2470                     NotificationChannelGroup group = (NotificationChannelGroup) args.arg3;
2471                     int modificationType = (int) args.arg4;
2472                     args.recycle();
2473                     onNotificationChannelGroupModified(pkgName, user, group, modificationType);
2474                 } break;
2475 
2476                 case MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED: {
2477                     onSilentStatusBarIconsVisibilityChanged((Boolean) msg.obj);
2478                 } break;
2479             }
2480         }
2481     }
2482 }
2483