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