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