• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.service.notification;
18 
19 import android.annotation.CurrentTimeMillisLong;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.SdkConstant;
23 import android.annotation.SystemApi;
24 import android.annotation.TestApi;
25 import android.annotation.UnsupportedAppUsage;
26 import android.app.ActivityManager;
27 import android.app.INotificationManager;
28 import android.app.Notification;
29 import android.app.Notification.Builder;
30 import android.app.NotificationChannel;
31 import android.app.NotificationChannelGroup;
32 import android.app.NotificationManager;
33 import android.app.Person;
34 import android.app.Service;
35 import android.companion.CompanionDeviceManager;
36 import android.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 
1518         private static final int PARCEL_VERSION = 2;
1519 
Ranking()1520         public Ranking() { }
1521 
1522         // You can parcel it, but it's not Parcelable
1523         /** @hide */
1524         @VisibleForTesting
writeToParcel(Parcel out, int flags)1525         public void writeToParcel(Parcel out, int flags) {
1526             final long start = out.dataPosition();
1527             out.writeInt(PARCEL_VERSION);
1528             out.writeString(mKey);
1529             out.writeInt(mRank);
1530             out.writeBoolean(mIsAmbient);
1531             out.writeBoolean(mMatchesInterruptionFilter);
1532             out.writeInt(mVisibilityOverride);
1533             out.writeInt(mSuppressedVisualEffects);
1534             out.writeInt(mImportance);
1535             out.writeCharSequence(mImportanceExplanation);
1536             out.writeString(mOverrideGroupKey);
1537             out.writeParcelable(mChannel, flags);
1538             out.writeStringList(mOverridePeople);
1539             out.writeTypedList(mSnoozeCriteria, flags);
1540             out.writeBoolean(mShowBadge);
1541             out.writeInt(mUserSentiment);
1542             out.writeBoolean(mHidden);
1543             out.writeLong(mLastAudiblyAlertedMs);
1544             out.writeBoolean(mNoisy);
1545             out.writeTypedList(mSmartActions, flags);
1546             out.writeCharSequenceList(mSmartReplies);
1547             out.writeBoolean(mCanBubble);
1548         }
1549 
1550         /** @hide */
1551         @VisibleForTesting
Ranking(Parcel in)1552         public Ranking(Parcel in) {
1553             final ClassLoader cl = getClass().getClassLoader();
1554 
1555             final int version = in.readInt();
1556             if (version != PARCEL_VERSION) {
1557                 throw new IllegalArgumentException("malformed Ranking parcel: " + in + " version "
1558                         + version + ", expected " + PARCEL_VERSION);
1559             }
1560             mKey = in.readString();
1561             mRank = in.readInt();
1562             mIsAmbient = in.readBoolean();
1563             mMatchesInterruptionFilter = in.readBoolean();
1564             mVisibilityOverride = in.readInt();
1565             mSuppressedVisualEffects = in.readInt();
1566             mImportance = in.readInt();
1567             mImportanceExplanation = in.readCharSequence(); // may be null
1568             mOverrideGroupKey = in.readString(); // may be null
1569             mChannel = (NotificationChannel) in.readParcelable(cl); // may be null
1570             mOverridePeople = in.createStringArrayList();
1571             mSnoozeCriteria = in.createTypedArrayList(SnoozeCriterion.CREATOR);
1572             mShowBadge = in.readBoolean();
1573             mUserSentiment = in.readInt();
1574             mHidden = in.readBoolean();
1575             mLastAudiblyAlertedMs = in.readLong();
1576             mNoisy = in.readBoolean();
1577             mSmartActions = in.createTypedArrayList(Notification.Action.CREATOR);
1578             mSmartReplies = in.readCharSequenceList();
1579             mCanBubble = in.readBoolean();
1580         }
1581 
1582 
1583         /**
1584          * Returns the key of the notification this Ranking applies to.
1585          */
getKey()1586         public String getKey() {
1587             return mKey;
1588         }
1589 
1590         /**
1591          * Returns the rank of the notification.
1592          *
1593          * @return the rank of the notification, that is the 0-based index in
1594          *     the list of active notifications.
1595          */
getRank()1596         public int getRank() {
1597             return mRank;
1598         }
1599 
1600         /**
1601          * Returns whether the notification is an ambient notification, that is
1602          * a notification that doesn't require the user's immediate attention.
1603          */
isAmbient()1604         public boolean isAmbient() {
1605             return mIsAmbient;
1606         }
1607 
1608         /**
1609          * Returns the user specified visibility for the package that posted
1610          * this notification, or
1611          * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if
1612          * no such preference has been expressed.
1613          * @hide
1614          */
1615         @UnsupportedAppUsage
getVisibilityOverride()1616         public int getVisibilityOverride() {
1617             return mVisibilityOverride;
1618         }
1619 
1620         /**
1621          * Returns the type(s) of visual effects that should be suppressed for this notification.
1622          * See {@link NotificationManager.Policy}, e.g.
1623          * {@link NotificationManager.Policy#SUPPRESSED_EFFECT_LIGHTS}.
1624          */
getSuppressedVisualEffects()1625         public int getSuppressedVisualEffects() {
1626             return mSuppressedVisualEffects;
1627         }
1628 
1629         /**
1630          * Returns whether the notification matches the user's interruption
1631          * filter.
1632          *
1633          * @return {@code true} if the notification is allowed by the filter, or
1634          * {@code false} if it is blocked.
1635          */
matchesInterruptionFilter()1636         public boolean matchesInterruptionFilter() {
1637             return mMatchesInterruptionFilter;
1638         }
1639 
1640         /**
1641          * Returns the importance of the notification, which dictates its
1642          * modes of presentation, see: {@link NotificationManager#IMPORTANCE_DEFAULT}, etc.
1643          *
1644          * @return the importance of the notification
1645          */
getImportance()1646         public @NotificationManager.Importance int getImportance() {
1647             return mImportance;
1648         }
1649 
1650         /**
1651          * If the importance has been overridden by user preference, then this will be non-null,
1652          * and should be displayed to the user.
1653          *
1654          * @return the explanation for the importance, or null if it is the natural importance
1655          */
getImportanceExplanation()1656         public CharSequence getImportanceExplanation() {
1657             return mImportanceExplanation;
1658         }
1659 
1660         /**
1661          * If the system has overridden the group key, then this will be non-null, and this
1662          * key should be used to bundle notifications.
1663          */
getOverrideGroupKey()1664         public String getOverrideGroupKey() {
1665             return mOverrideGroupKey;
1666         }
1667 
1668         /**
1669          * Returns the notification channel this notification was posted to, which dictates
1670          * notification behavior and presentation.
1671          */
getChannel()1672         public NotificationChannel getChannel() {
1673             return mChannel;
1674         }
1675 
1676         /**
1677          * Returns how the system thinks the user feels about notifications from the
1678          * channel provided by {@link #getChannel()}. You can use this information to expose
1679          * controls to help the user block this channel's notifications, if the sentiment is
1680          * {@link #USER_SENTIMENT_NEGATIVE}, or emphasize this notification if the sentiment is
1681          * {@link #USER_SENTIMENT_POSITIVE}.
1682          */
getUserSentiment()1683         public int getUserSentiment() {
1684             return mUserSentiment;
1685         }
1686 
1687         /**
1688          * If the {@link NotificationAssistantService} has added people to this notification, then
1689          * this will be non-null.
1690          * @hide
1691          * @removed
1692          */
1693         @SystemApi
getAdditionalPeople()1694         public List<String> getAdditionalPeople() {
1695             return mOverridePeople;
1696         }
1697 
1698         /**
1699          * Returns snooze criteria provided by the {@link NotificationAssistantService}. If your
1700          * user interface displays options for snoozing notifications these criteria should be
1701          * displayed as well.
1702          * @hide
1703          * @removed
1704          */
1705         @SystemApi
getSnoozeCriteria()1706         public List<SnoozeCriterion> getSnoozeCriteria() {
1707             return mSnoozeCriteria;
1708         }
1709 
1710         /**
1711          * Returns a list of smart {@link Notification.Action} that can be added by the
1712          * {@link NotificationAssistantService}
1713          */
getSmartActions()1714         public @NonNull List<Notification.Action> getSmartActions() {
1715             return mSmartActions;
1716         }
1717 
1718         /**
1719          * Returns a list of smart replies that can be added by the
1720          * {@link NotificationAssistantService}
1721          */
getSmartReplies()1722         public @NonNull List<CharSequence> getSmartReplies() {
1723             return mSmartReplies;
1724         }
1725 
1726         /**
1727          * Returns whether this notification can be displayed as a badge.
1728          *
1729          * @return true if the notification can be displayed as a badge, false otherwise.
1730          */
canShowBadge()1731         public boolean canShowBadge() {
1732             return mShowBadge;
1733         }
1734 
1735         /**
1736          * Returns whether the app that posted this notification is suspended, so this notification
1737          * should be hidden.
1738          *
1739          * @return true if the notification should be hidden, false otherwise.
1740          */
isSuspended()1741         public boolean isSuspended() {
1742             return mHidden;
1743         }
1744 
1745         /**
1746          * Returns the last time this notification alerted the user via sound or vibration.
1747          *
1748          * @return the time of the last alerting behavior, in milliseconds.
1749          */
1750         @CurrentTimeMillisLong
getLastAudiblyAlertedMillis()1751         public long getLastAudiblyAlertedMillis() {
1752             return mLastAudiblyAlertedMs;
1753         }
1754 
1755         /**
1756          * Returns whether the user has allowed bubbles globally, at the app level, and at the
1757          * channel level for this notification.
1758          *
1759          * <p>This does not take into account the current importance of the notification, the
1760          * current DND state, or whether the posting app is foreground.</p>
1761          */
canBubble()1762         public boolean canBubble() {
1763             return mCanBubble;
1764         }
1765 
1766         /** @hide */
isNoisy()1767         public boolean isNoisy() {
1768             return mNoisy;
1769         }
1770 
1771         /**
1772          * @hide
1773          */
1774         @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)1775         public void populate(String key, int rank, boolean matchesInterruptionFilter,
1776                 int visibilityOverride, int suppressedVisualEffects, int importance,
1777                 CharSequence explanation, String overrideGroupKey,
1778                 NotificationChannel channel, ArrayList<String> overridePeople,
1779                 ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
1780                 int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
1781                 boolean noisy, ArrayList<Notification.Action> smartActions,
1782                 ArrayList<CharSequence> smartReplies, boolean canBubble) {
1783             mKey = key;
1784             mRank = rank;
1785             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
1786             mMatchesInterruptionFilter = matchesInterruptionFilter;
1787             mVisibilityOverride = visibilityOverride;
1788             mSuppressedVisualEffects = suppressedVisualEffects;
1789             mImportance = importance;
1790             mImportanceExplanation = explanation;
1791             mOverrideGroupKey = overrideGroupKey;
1792             mChannel = channel;
1793             mOverridePeople = overridePeople;
1794             mSnoozeCriteria = snoozeCriteria;
1795             mShowBadge = showBadge;
1796             mUserSentiment = userSentiment;
1797             mHidden = hidden;
1798             mLastAudiblyAlertedMs = lastAudiblyAlertedMs;
1799             mNoisy = noisy;
1800             mSmartActions = smartActions;
1801             mSmartReplies = smartReplies;
1802             mCanBubble = canBubble;
1803         }
1804 
1805         /**
1806          * @hide
1807          */
1808         public void populate(Ranking other) {
1809             populate(other.mKey,
1810                     other.mRank,
1811                     other.mMatchesInterruptionFilter,
1812                     other.mVisibilityOverride,
1813                     other.mSuppressedVisualEffects,
1814                     other.mImportance,
1815                     other.mImportanceExplanation,
1816                     other.mOverrideGroupKey,
1817                     other.mChannel,
1818                     other.mOverridePeople,
1819                     other.mSnoozeCriteria,
1820                     other.mShowBadge,
1821                     other.mUserSentiment,
1822                     other.mHidden,
1823                     other.mLastAudiblyAlertedMs,
1824                     other.mNoisy,
1825                     other.mSmartActions,
1826                     other.mSmartReplies,
1827                     other.mCanBubble);
1828         }
1829 
1830         /**
1831          * {@hide}
1832          */
1833         public static String importanceToString(int importance) {
1834             switch (importance) {
1835                 case NotificationManager.IMPORTANCE_UNSPECIFIED:
1836                     return "UNSPECIFIED";
1837                 case NotificationManager.IMPORTANCE_NONE:
1838                     return "NONE";
1839                 case NotificationManager.IMPORTANCE_MIN:
1840                     return "MIN";
1841                 case NotificationManager.IMPORTANCE_LOW:
1842                     return "LOW";
1843                 case NotificationManager.IMPORTANCE_DEFAULT:
1844                     return "DEFAULT";
1845                 case NotificationManager.IMPORTANCE_HIGH:
1846                 case NotificationManager.IMPORTANCE_MAX:
1847                     return "HIGH";
1848                 default:
1849                     return "UNKNOWN(" + String.valueOf(importance) + ")";
1850             }
1851         }
1852 
1853         @Override
1854         public boolean equals(Object o) {
1855             if (this == o) return true;
1856             if (o == null || getClass() != o.getClass()) return false;
1857 
1858             Ranking other = (Ranking) o;
1859             return Objects.equals(mKey, other.mKey)
1860                     && Objects.equals(mRank, other.mRank)
1861                     && Objects.equals(mMatchesInterruptionFilter, other.mMatchesInterruptionFilter)
1862                     && Objects.equals(mVisibilityOverride, other.mVisibilityOverride)
1863                     && Objects.equals(mSuppressedVisualEffects, other.mSuppressedVisualEffects)
1864                     && Objects.equals(mImportance, other.mImportance)
1865                     && Objects.equals(mImportanceExplanation, other.mImportanceExplanation)
1866                     && Objects.equals(mOverrideGroupKey, other.mOverrideGroupKey)
1867                     && Objects.equals(mChannel, other.mChannel)
1868                     && Objects.equals(mOverridePeople, other.mOverridePeople)
1869                     && Objects.equals(mSnoozeCriteria, other.mSnoozeCriteria)
1870                     && Objects.equals(mShowBadge, other.mShowBadge)
1871                     && Objects.equals(mUserSentiment, other.mUserSentiment)
1872                     && Objects.equals(mHidden, other.mHidden)
1873                     && Objects.equals(mLastAudiblyAlertedMs, other.mLastAudiblyAlertedMs)
1874                     && Objects.equals(mNoisy, other.mNoisy)
1875                     // Action.equals() doesn't exist so let's just compare list lengths
1876                     && ((mSmartActions == null ? 0 : mSmartActions.size())
1877                         == (other.mSmartActions == null ? 0 : other.mSmartActions.size()))
1878                     && Objects.equals(mSmartReplies, other.mSmartReplies)
1879                     && Objects.equals(mCanBubble, other.mCanBubble);
1880         }
1881     }
1882 
1883     /**
1884      * Provides access to ranking information on currently active
1885      * notifications.
1886      *
1887      * <p>
1888      * Note that this object represents a ranking snapshot that only applies to
1889      * notifications active at the time of retrieval.
1890      */
1891     public static class RankingMap implements Parcelable {
1892         private ArrayList<String> mOrderedKeys = new ArrayList<>();
1893         // Note: all String keys should be intern'd as pointers into mOrderedKeys
1894         private ArrayMap<String, Ranking> mRankings = new ArrayMap<>();
1895 
1896         /**
1897          * @hide
1898          */
1899         public RankingMap(Ranking[] rankings) {
1900             for (int i = 0; i < rankings.length; i++) {
1901                 final String key = rankings[i].getKey();
1902                 mOrderedKeys.add(key);
1903                 mRankings.put(key, rankings[i]);
1904             }
1905         }
1906 
1907         // -- parcelable interface --
1908 
1909         private RankingMap(Parcel in) {
1910             final ClassLoader cl = getClass().getClassLoader();
1911             final int count = in.readInt();
1912             mOrderedKeys.ensureCapacity(count);
1913             mRankings.ensureCapacity(count);
1914             for (int i = 0; i < count; i++) {
1915                 final Ranking r = new Ranking(in);
1916                 final String key = r.getKey();
1917                 mOrderedKeys.add(key);
1918                 mRankings.put(key, r);
1919             }
1920         }
1921 
1922         @Override
1923         public boolean equals(Object o) {
1924             if (this == o) return true;
1925             if (o == null || getClass() != o.getClass()) return false;
1926 
1927             RankingMap other = (RankingMap) o;
1928 
1929             return mOrderedKeys.equals(other.mOrderedKeys)
1930                     && mRankings.equals(other.mRankings);
1931 
1932         }
1933 
1934         @Override
1935         public int describeContents() {
1936             return 0;
1937         }
1938 
1939         @Override
1940         public void writeToParcel(Parcel out, int flags) {
1941             final int count = mOrderedKeys.size();
1942             out.writeInt(count);
1943             for (int i = 0; i < count; i++) {
1944                 mRankings.get(mOrderedKeys.get(i)).writeToParcel(out, flags);
1945             }
1946         }
1947 
1948         public static final @android.annotation.NonNull Creator<RankingMap> CREATOR = new Creator<RankingMap>() {
1949             @Override
1950             public RankingMap createFromParcel(Parcel source) {
1951                 return new RankingMap(source);
1952             }
1953 
1954             @Override
1955             public RankingMap[] newArray(int size) {
1956                 return new RankingMap[size];
1957             }
1958         };
1959 
1960         /**
1961          * Request the list of notification keys in their current ranking
1962          * order.
1963          *
1964          * @return An array of active notification keys, in their ranking order.
1965          */
1966         public String[] getOrderedKeys() {
1967             return mOrderedKeys.toArray(new String[0]);
1968         }
1969 
1970         /**
1971          * Populates outRanking with ranking information for the notification
1972          * with the given key.
1973          *
1974          * @return true if a valid key has been passed and outRanking has
1975          * been populated; false otherwise
1976          */
1977         public boolean getRanking(String key, Ranking outRanking) {
1978             if (mRankings.containsKey(key)) {
1979                 outRanking.populate(mRankings.get(key));
1980                 return true;
1981             }
1982             return false;
1983         }
1984 
1985         /**
1986          * Get a reference to the actual Ranking object corresponding to the key.
1987          * Used only by unit tests.
1988          *
1989          * @hide
1990          */
1991         @VisibleForTesting
1992         public Ranking getRawRankingObject(String key) {
1993             return mRankings.get(key);
1994         }
1995     }
1996 
1997     private final class MyHandler extends Handler {
1998         public static final int MSG_ON_NOTIFICATION_POSTED = 1;
1999         public static final int MSG_ON_NOTIFICATION_REMOVED = 2;
2000         public static final int MSG_ON_LISTENER_CONNECTED = 3;
2001         public static final int MSG_ON_NOTIFICATION_RANKING_UPDATE = 4;
2002         public static final int MSG_ON_LISTENER_HINTS_CHANGED = 5;
2003         public static final int MSG_ON_INTERRUPTION_FILTER_CHANGED = 6;
2004         public static final int MSG_ON_NOTIFICATION_CHANNEL_MODIFIED = 7;
2005         public static final int MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED = 8;
2006         public static final int MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED = 9;
2007 
2008         public MyHandler(Looper looper) {
2009             super(looper, null, false);
2010         }
2011 
2012         @Override
2013         public void handleMessage(Message msg) {
2014             if (!isConnected) {
2015                 return;
2016             }
2017             switch (msg.what) {
2018                 case MSG_ON_NOTIFICATION_POSTED: {
2019                     SomeArgs args = (SomeArgs) msg.obj;
2020                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
2021                     RankingMap rankingMap = (RankingMap) args.arg2;
2022                     args.recycle();
2023                     onNotificationPosted(sbn, rankingMap);
2024                 } break;
2025 
2026                 case MSG_ON_NOTIFICATION_REMOVED: {
2027                     SomeArgs args = (SomeArgs) msg.obj;
2028                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
2029                     RankingMap rankingMap = (RankingMap) args.arg2;
2030                     int reason = (int) args.arg3;
2031                     NotificationStats stats = (NotificationStats) args.arg4;
2032                     args.recycle();
2033                     onNotificationRemoved(sbn, rankingMap, stats, reason);
2034                 } break;
2035 
2036                 case MSG_ON_LISTENER_CONNECTED: {
2037                     onListenerConnected();
2038                 } break;
2039 
2040                 case MSG_ON_NOTIFICATION_RANKING_UPDATE: {
2041                     RankingMap rankingMap = (RankingMap) msg.obj;
2042                     onNotificationRankingUpdate(rankingMap);
2043                 } break;
2044 
2045                 case MSG_ON_LISTENER_HINTS_CHANGED: {
2046                     final int hints = msg.arg1;
2047                     onListenerHintsChanged(hints);
2048                 } break;
2049 
2050                 case MSG_ON_INTERRUPTION_FILTER_CHANGED: {
2051                     final int interruptionFilter = msg.arg1;
2052                     onInterruptionFilterChanged(interruptionFilter);
2053                 } break;
2054 
2055                 case MSG_ON_NOTIFICATION_CHANNEL_MODIFIED: {
2056                     SomeArgs args = (SomeArgs) msg.obj;
2057                     String pkgName = (String) args.arg1;
2058                     UserHandle user= (UserHandle) args.arg2;
2059                     NotificationChannel channel = (NotificationChannel) args.arg3;
2060                     int modificationType = (int) args.arg4;
2061                     onNotificationChannelModified(pkgName, user, channel, modificationType);
2062                 } break;
2063 
2064                 case MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED: {
2065                     SomeArgs args = (SomeArgs) msg.obj;
2066                     String pkgName = (String) args.arg1;
2067                     UserHandle user = (UserHandle) args.arg2;
2068                     NotificationChannelGroup group = (NotificationChannelGroup) args.arg3;
2069                     int modificationType = (int) args.arg4;
2070                     onNotificationChannelGroupModified(pkgName, user, group, modificationType);
2071                 } break;
2072 
2073                 case MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED: {
2074                     onSilentStatusBarIconsVisibilityChanged((Boolean) msg.obj);
2075                 } break;
2076             }
2077         }
2078     }
2079 }
2080