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