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