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