/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.service.notification;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.Person;
import android.app.Service;
import android.companion.CompanionDeviceManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.widget.RemoteViews;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

A service that receives calls from the system when new notifications are posted or removed, or their ranking changed.

To extend this class, you must declare the service in your manifest file with the BIND_NOTIFICATION_LISTENER_SERVICE.BIND_NOTIFICATION_LISTENER_SERVICE permission and include an intent filter with the SERVICE_INTERFACE action. For example:

<service android:name=".NotificationListener"
         android:label="@string/service_name"
         android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
    <intent-filter>
        <action android:name="android.service.notification.NotificationListenerService" />
    </intent-filter>
</service>

The service should wait for the onListenerConnected() event before performing any operations. The requestRebind(ComponentName) method is the only one that is safe to call before onListenerConnected() or after onListenerDisconnected().

Notification listeners cannot get notification access or be bound by the system on low-RAM devices. The system also ignores notification listeners running in a work profile. A DevicePolicyManager might block notifications originating from a work profile.

/** * A service that receives calls from the system when new notifications are * posted or removed, or their ranking changed. * <p>To extend this class, you must declare the service in your manifest file with * the {@link android.Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE} permission * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> * <pre> * &lt;service android:name=".NotificationListener" * android:label="&#64;string/service_name" * android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> * &lt;intent-filter> * &lt;action android:name="android.service.notification.NotificationListenerService" /> * &lt;/intent-filter> * &lt;/service></pre> * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing any operations. The {@link #requestRebind(ComponentName)} * method is the <i>only</i> one that is safe to call before {@link #onListenerConnected()} * or after {@link #onListenerDisconnected()}. * </p> * <p> Notification listeners cannot get notification access or be bound by the system on * {@linkplain ActivityManager#isLowRamDevice() low-RAM} devices. The system also ignores * notification listeners running in a work profile. A * {@link android.app.admin.DevicePolicyManager} might block notifications originating from a work * profile.</p> */
public abstract class NotificationListenerService extends Service { private final String TAG = getClass().getSimpleName();
Interruption filter constant - Normal interruption filter.
/** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * Normal interruption filter. */
public static final int INTERRUPTION_FILTER_ALL = NotificationManager.INTERRUPTION_FILTER_ALL;
Interruption filter constant - Priority interruption filter.
/** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * Priority interruption filter. */
public static final int INTERRUPTION_FILTER_PRIORITY = NotificationManager.INTERRUPTION_FILTER_PRIORITY;
Interruption filter constant - No interruptions filter.
/** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * No interruptions filter. */
public static final int INTERRUPTION_FILTER_NONE = NotificationManager.INTERRUPTION_FILTER_NONE;
Interruption filter constant - Alarms only interruption filter.
/** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * Alarms only interruption filter. */
public static final int INTERRUPTION_FILTER_ALARMS = NotificationManager.INTERRUPTION_FILTER_ALARMS;
Interruption filter constant - returned when the value is unavailable for any reason. For example, before the notification listener is connected. {@see #onListenerConnected()}
/** {@link #getCurrentInterruptionFilter() Interruption filter} constant - returned when * the value is unavailable for any reason. For example, before the notification listener * is connected. * * {@see #onListenerConnected()} */
public static final int INTERRUPTION_FILTER_UNKNOWN = NotificationManager.INTERRUPTION_FILTER_UNKNOWN;
Listener hints constant - the primary device UI should disable notification sound, vibrating and other visual or aural effects. This does not change the interruption filter, only the effects.
/** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI * should disable notification sound, vibrating and other visual or aural effects. * This does not change the interruption filter, only the effects. **/
public static final int HINT_HOST_DISABLE_EFFECTS = 1;
Listener hints constant - the primary device UI should disable notification sound, but not phone calls. This does not change the interruption filter, only the effects.
/** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI * should disable notification sound, but not phone calls. * This does not change the interruption filter, only the effects. **/
public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 1 << 1;
Listener hints constant - the primary device UI should disable phone call sounds, buyt not notification sound. This does not change the interruption filter, only the effects.
/** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI * should disable phone call sounds, buyt not notification sound. * This does not change the interruption filter, only the effects. **/
public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 1 << 2;
Whether notification suppressed by DND should not interruption visually when the screen is off.
Deprecated:Use the more specific visual effects in Policy.
/** * Whether notification suppressed by DND should not interruption visually when the screen is * off. * * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}. */
@Deprecated public static final int SUPPRESSED_EFFECT_SCREEN_OFF = NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
Whether notification suppressed by DND should not interruption visually when the screen is on.
Deprecated:Use the more specific visual effects in Policy.
/** * Whether notification suppressed by DND should not interruption visually when the screen is * on. * * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}. */
@Deprecated public static final int SUPPRESSED_EFFECT_SCREEN_ON = NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; // Notification cancellation reasons
Notification was canceled by the status bar reporting a notification click.
/** Notification was canceled by the status bar reporting a notification click. */
public static final int REASON_CLICK = 1;
Notification was canceled by the status bar reporting a user dismissal.
/** Notification was canceled by the status bar reporting a user dismissal. */
public static final int REASON_CANCEL = 2;
Notification was canceled by the status bar reporting a user dismiss all.
/** Notification was canceled by the status bar reporting a user dismiss all. */
public static final int REASON_CANCEL_ALL = 3;
Notification was canceled by the status bar reporting an inflation error.
/** Notification was canceled by the status bar reporting an inflation error. */
public static final int REASON_ERROR = 4;
Notification was canceled by the package manager modifying the package.
/** Notification was canceled by the package manager modifying the package. */
public static final int REASON_PACKAGE_CHANGED = 5;
Notification was canceled by the owning user context being stopped.
/** Notification was canceled by the owning user context being stopped. */
public static final int REASON_USER_STOPPED = 6;
Notification was canceled by the user banning the package.
/** Notification was canceled by the user banning the package. */
public static final int REASON_PACKAGE_BANNED = 7;
Notification was canceled by the app canceling this specific notification.
/** Notification was canceled by the app canceling this specific notification. */
public static final int REASON_APP_CANCEL = 8;
Notification was canceled by the app cancelling all its notifications.
/** Notification was canceled by the app cancelling all its notifications. */
public static final int REASON_APP_CANCEL_ALL = 9;
Notification was canceled by a listener reporting a user dismissal.
/** Notification was canceled by a listener reporting a user dismissal. */
public static final int REASON_LISTENER_CANCEL = 10;
Notification was canceled by a listener reporting a user dismiss all.
/** Notification was canceled by a listener reporting a user dismiss all. */
public static final int REASON_LISTENER_CANCEL_ALL = 11;
Notification was canceled because it was a member of a canceled group.
/** Notification was canceled because it was a member of a canceled group. */
public static final int REASON_GROUP_SUMMARY_CANCELED = 12;
Notification was canceled because it was an invisible member of a group.
/** Notification was canceled because it was an invisible member of a group. */
public static final int REASON_GROUP_OPTIMIZATION = 13;
Notification was canceled by the device administrator suspending the package.
/** Notification was canceled by the device administrator suspending the package. */
public static final int REASON_PACKAGE_SUSPENDED = 14;
Notification was canceled by the owning managed profile being turned off.
/** Notification was canceled by the owning managed profile being turned off. */
public static final int REASON_PROFILE_TURNED_OFF = 15;
Autobundled summary notification was canceled because its group was unbundled
/** Autobundled summary notification was canceled because its group was unbundled */
public static final int REASON_UNAUTOBUNDLED = 16;
Notification was canceled by the user banning the channel.
/** Notification was canceled by the user banning the channel. */
public static final int REASON_CHANNEL_BANNED = 17;
Notification was snoozed.
/** Notification was snoozed. */
public static final int REASON_SNOOZED = 18;
Notification was canceled due to timeout
/** Notification was canceled due to timeout */
public static final int REASON_TIMEOUT = 19;
The full trim of the StatusBarNotification including all its features.
@hide
@removed
/** * The full trim of the StatusBarNotification including all its features. * * @hide * @removed */
@SystemApi public static final int TRIM_FULL = 0;
@hide
@removed
/** * A light trim of the StatusBarNotification excluding the following features: * * <ol> * <li>{@link Notification#tickerView tickerView}</li> * <li>{@link Notification#contentView contentView}</li> * <li>{@link Notification#largeIcon largeIcon}</li> * <li>{@link Notification#bigContentView bigContentView}</li> * <li>{@link Notification#headsUpContentView headsUpContentView}</li> * <li>{@link Notification#EXTRA_LARGE_ICON extras[EXTRA_LARGE_ICON]}</li> * <li>{@link Notification#EXTRA_LARGE_ICON_BIG extras[EXTRA_LARGE_ICON_BIG]}</li> * <li>{@link Notification#EXTRA_PICTURE extras[EXTRA_PICTURE]}</li> * <li>{@link Notification#EXTRA_BIG_TEXT extras[EXTRA_BIG_TEXT]}</li> * </ol> * * @hide * @removed */
@SystemApi public static final int TRIM_LIGHT = 1;
@hide
/** @hide */
@IntDef(prefix = { "NOTIFICATION_CHANNEL_OR_GROUP_" }, value = { NOTIFICATION_CHANNEL_OR_GROUP_ADDED, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED, NOTIFICATION_CHANNEL_OR_GROUP_DELETED }) @Retention(RetentionPolicy.SOURCE) public @interface ChannelOrGroupModificationTypes {} /** * Channel or group modification reason provided to * {@link #onNotificationChannelModified(String, UserHandle,NotificationChannel, int)} or * {@link #onNotificationChannelGroupModified(String, UserHandle, NotificationChannelGroup, * int)}- the provided object was created. */ public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1; /** * Channel or group modification reason provided to * {@link #onNotificationChannelModified(String, UserHandle, NotificationChannel, int)} or * {@link #onNotificationChannelGroupModified(String, UserHandle,NotificationChannelGroup, int)} * - the provided object was updated. */ public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2; /** * Channel or group modification reason provided to * {@link #onNotificationChannelModified(String, UserHandle, NotificationChannel, int)} or * {@link #onNotificationChannelGroupModified(String, UserHandle, NotificationChannelGroup, * int)}- the provided object was deleted. */ public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3; private final Object mLock = new Object(); private Handler mHandler;
@hide
/** @hide */
protected NotificationListenerWrapper mWrapper = null; private boolean isConnected = false; @GuardedBy("mLock") private RankingMap mRankingMap;
@hide
/** * @hide */
protected INotificationManager mNoMan;
Only valid after a successful call to (@link registerAsService}.
@hide
/** * Only valid after a successful call to (@link registerAsService}. * @hide */
protected int mCurrentUser;
This context is required for system services since NotificationListenerService isn't started as a real Service and hence no context is available..
@hide
/** * This context is required for system services since NotificationListenerService isn't * started as a real Service and hence no context is available.. * @hide */
protected Context mSystemContext;
The Intent that must be declared as handled by the service.
/** * The {@link Intent} that must be declared as handled by the service. */
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) public static final String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); mHandler = new MyHandler(getMainLooper()); }
Implement this method to learn about new notifications as they are posted by apps.
Params:
  • sbn – A data structure encapsulating the original Notification object as well as its identifying information (tag and id) and source (package name).
/** * Implement this method to learn about new notifications as they are posted by apps. * * @param sbn A data structure encapsulating the original {@link android.app.Notification} * object as well as its identifying information (tag and id) and source * (package name). */
public void onNotificationPosted(StatusBarNotification sbn) { // optional }
Implement this method to learn about new notifications as they are posted by apps.
Params:
  • sbn – A data structure encapsulating the original Notification object as well as its identifying information (tag and id) and source (package name).
  • rankingMap – The current ranking map that can be used to retrieve ranking information for active notifications, including the newly posted one.
/** * Implement this method to learn about new notifications as they are posted by apps. * * @param sbn A data structure encapsulating the original {@link android.app.Notification} * object as well as its identifying information (tag and id) and source * (package name). * @param rankingMap The current ranking map that can be used to retrieve ranking information * for active notifications, including the newly posted one. */
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { onNotificationPosted(sbn); }
Implement this method to learn when notifications are removed.

This might occur because the user has dismissed the notification using system UI (or another notification listener) or because the app has withdrawn the notification.

NOTE: The StatusBarNotification object you receive will be "light"; that is, the result from StatusBarNotification.getNotification may be missing some heavyweight fields such as Notification.contentView and Notification.largeIcon. However, all other fields on StatusBarNotification, sufficient to match this call with a prior call to onNotificationPosted(StatusBarNotification), will be intact.

Params:
  • sbn – A data structure encapsulating at least the original information (tag and id) and source (package name) used to post the Notification that was just removed.
/** * Implement this method to learn when notifications are removed. * <p> * This might occur because the user has dismissed the notification using system UI (or another * notification listener) or because the app has withdrawn the notification. * <p> * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight * fields such as {@link android.app.Notification#contentView} and * {@link android.app.Notification#largeIcon}. However, all other fields on * {@link StatusBarNotification}, sufficient to match this call with a prior call to * {@link #onNotificationPosted(StatusBarNotification)}, will be intact. * * @param sbn A data structure encapsulating at least the original information (tag and id) * and source (package name) used to post the {@link android.app.Notification} that * was just removed. */
public void onNotificationRemoved(StatusBarNotification sbn) { // optional }
Implement this method to learn when notifications are removed.

This might occur because the user has dismissed the notification using system UI (or another notification listener) or because the app has withdrawn the notification.

NOTE: The StatusBarNotification object you receive will be "light"; that is, the result from StatusBarNotification.getNotification may be missing some heavyweight fields such as Notification.contentView and Notification.largeIcon. However, all other fields on StatusBarNotification, sufficient to match this call with a prior call to onNotificationPosted(StatusBarNotification), will be intact.

Params:
  • sbn – A data structure encapsulating at least the original information (tag and id) and source (package name) used to post the Notification that was just removed.
  • rankingMap – The current ranking map that can be used to retrieve ranking information for active notifications.
/** * Implement this method to learn when notifications are removed. * <p> * This might occur because the user has dismissed the notification using system UI (or another * notification listener) or because the app has withdrawn the notification. * <p> * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight * fields such as {@link android.app.Notification#contentView} and * {@link android.app.Notification#largeIcon}. However, all other fields on * {@link StatusBarNotification}, sufficient to match this call with a prior call to * {@link #onNotificationPosted(StatusBarNotification)}, will be intact. * * @param sbn A data structure encapsulating at least the original information (tag and id) * and source (package name) used to post the {@link android.app.Notification} that * was just removed. * @param rankingMap The current ranking map that can be used to retrieve ranking information * for active notifications. * */
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) { onNotificationRemoved(sbn); }
Implement this method to learn when notifications are removed and why.

This might occur because the user has dismissed the notification using system UI (or another notification listener) or because the app has withdrawn the notification.

NOTE: The StatusBarNotification object you receive will be "light"; that is, the result from StatusBarNotification.getNotification may be missing some heavyweight fields such as Notification.contentView and Notification.largeIcon. However, all other fields on StatusBarNotification, sufficient to match this call with a prior call to onNotificationPosted(StatusBarNotification), will be intact.

Params:
  • sbn – A data structure encapsulating at least the original information (tag and id) and source (package name) used to post the Notification that was just removed.
  • rankingMap – The current ranking map that can be used to retrieve ranking information for active notifications.
  • reason – see REASON_LISTENER_CANCEL, etc.
/** * Implement this method to learn when notifications are removed and why. * <p> * This might occur because the user has dismissed the notification using system UI (or another * notification listener) or because the app has withdrawn the notification. * <p> * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight * fields such as {@link android.app.Notification#contentView} and * {@link android.app.Notification#largeIcon}. However, all other fields on * {@link StatusBarNotification}, sufficient to match this call with a prior call to * {@link #onNotificationPosted(StatusBarNotification)}, will be intact. * ** @param sbn A data structure encapsulating at least the original information (tag and id) * and source (package name) used to post the {@link android.app.Notification} that * was just removed. * @param rankingMap The current ranking map that can be used to retrieve ranking information * for active notifications. * @param reason see {@link #REASON_LISTENER_CANCEL}, etc. */
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, int reason) { onNotificationRemoved(sbn, rankingMap); }
NotificationStats are not populated for notification listeners, so fall back to onNotificationRemoved(StatusBarNotification, RankingMap, int).
@hide
/** * NotificationStats are not populated for notification listeners, so fall back to * {@link #onNotificationRemoved(StatusBarNotification, RankingMap, int)}. * * @hide */
@TestApi public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, NotificationStats stats, int reason) { onNotificationRemoved(sbn, rankingMap, reason); }
Implement this method to learn about when the listener is enabled and connected to the notification manager. You are safe to call getActiveNotifications() at this time.
/** * Implement this method to learn about when the listener is enabled and connected to * the notification manager. You are safe to call {@link #getActiveNotifications()} * at this time. */
public void onListenerConnected() { // optional }
Implement this method to learn about when the listener is disconnected from the notification manager.You will not receive any events after this call, and may only call requestRebind(ComponentName) at this time.
/** * Implement this method to learn about when the listener is disconnected from the * notification manager.You will not receive any events after this call, and may only * call {@link #requestRebind(ComponentName)} at this time. */
public void onListenerDisconnected() { // optional }
Implement this method to be notified when the notification ranking changes.
Params:
  • rankingMap – The current ranking map that can be used to retrieve ranking information for active notifications.
/** * Implement this method to be notified when the notification ranking changes. * * @param rankingMap The current ranking map that can be used to retrieve ranking information * for active notifications. */
public void onNotificationRankingUpdate(RankingMap rankingMap) { // optional }
Implement this method to be notified when the Listener hints change.
Params:
/** * Implement this method to be notified when the * {@link #getCurrentListenerHints() Listener hints} change. * * @param hints The current {@link #getCurrentListenerHints() listener hints}. */
public void onListenerHintsChanged(int hints) { // optional }
Implement this method to learn about notification channel modifications.

The caller must have an associated device in order to receive this callback.

Params:
/** * Implement this method to learn about notification channel modifications. * * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated * device} in order to receive this callback. * * @param pkg The package the channel belongs to. * @param user The user on which the change was made. * @param channel The channel that has changed. * @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED}, * {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED}, * {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}. */
public void onNotificationChannelModified(String pkg, UserHandle user, NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType) { // optional }
Implement this method to learn about notification channel group modifications.

The caller must have an associated device in order to receive this callback.

Params:
/** * Implement this method to learn about notification channel group modifications. * * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated * device} in order to receive this callback. * * @param pkg The package the group belongs to. * @param user The user on which the change was made. * @param group The group that has changed. * @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED}, * {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED}, * {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}. */
public void onNotificationChannelGroupModified(String pkg, UserHandle user, NotificationChannelGroup group, @ChannelOrGroupModificationTypes int modificationType) { // optional }
Implement this method to be notified when the interruption filter changed.
Params:
/** * Implement this method to be notified when the * {@link #getCurrentInterruptionFilter() interruption filter} changed. * * @param interruptionFilter The current * {@link #getCurrentInterruptionFilter() interruption filter}. */
public void onInterruptionFilterChanged(int interruptionFilter) { // optional }
@hide
/** @hide */
protected final INotificationManager getNotificationInterface() { if (mNoMan == null) { mNoMan = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); } return mNoMan; }
Inform the notification manager about dismissal of a single notification.

Use this if your listener has a user interface that allows the user to dismiss individual notifications, similar to the behavior of Android's status bar and notification panel. It should be called after the user dismisses a single notification using your UI; upon being informed, the notification manager will actually remove the notification and you will get an onNotificationRemoved(StatusBarNotification) callback.

Note: If your listener allows the user to fire a notification's Notification.contentIntent by tapping/clicking/etc., you should call this method at that time if the Notification in question has the Notification.FLAG_AUTO_CANCEL flag set.

The service should wait for the onListenerConnected() event before performing this operation.

Params:
Deprecated:Use cancelNotification(String key) instead. Beginning with VERSION_CODES.LOLLIPOP this method will no longer cancel the notification. It will continue to cancel the notification for applications whose targetSdkVersion is earlier than VERSION_CODES.LOLLIPOP.
/** * Inform the notification manager about dismissal of a single notification. * <p> * Use this if your listener has a user interface that allows the user to dismiss individual * notifications, similar to the behavior of Android's status bar and notification panel. * It should be called after the user dismisses a single notification using your UI; * upon being informed, the notification manager will actually remove the notification * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. * <p> * <b>Note:</b> If your listener allows the user to fire a notification's * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call * this method at that time <i>if</i> the Notification in question has the * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @param pkg Package of the notifying app. * @param tag Tag of the notification as specified by the notifying app in * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}. * @param id ID of the notification as specified by the notifying app in * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}. * <p> * @deprecated Use {@link #cancelNotification(String key)} * instead. Beginning with {@link android.os.Build.VERSION_CODES#LOLLIPOP} this method will no longer * cancel the notification. It will continue to cancel the notification for applications * whose {@code targetSdkVersion} is earlier than {@link android.os.Build.VERSION_CODES#LOLLIPOP}. */
@Deprecated public final void cancelNotification(String pkg, String tag, int id) { if (!isBound()) return; try { getNotificationInterface().cancelNotificationFromListener( mWrapper, pkg, tag, id); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } }
Inform the notification manager about dismissal of a single notification.

Use this if your listener has a user interface that allows the user to dismiss individual notifications, similar to the behavior of Android's status bar and notification panel. It should be called after the user dismisses a single notification using your UI; upon being informed, the notification manager will actually remove the notification and you will get an onNotificationRemoved(StatusBarNotification) callback.

Note: If your listener allows the user to fire a notification's Notification.contentIntent by tapping/clicking/etc., you should call this method at that time if the Notification in question has the Notification.FLAG_AUTO_CANCEL flag set.

The service should wait for the onListenerConnected() event before performing this operation.

Params:
/** * Inform the notification manager about dismissal of a single notification. * <p> * Use this if your listener has a user interface that allows the user to dismiss individual * notifications, similar to the behavior of Android's status bar and notification panel. * It should be called after the user dismisses a single notification using your UI; * upon being informed, the notification manager will actually remove the notification * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. * <p> * <b>Note:</b> If your listener allows the user to fire a notification's * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call * this method at that time <i>if</i> the Notification in question has the * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set. * <p> * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @param key Notification to dismiss from {@link StatusBarNotification#getKey()}. */
public final void cancelNotification(String key) { if (!isBound()) return; try { getNotificationInterface().cancelNotificationsFromListener(mWrapper, new String[] { key }); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } }
Inform the notification manager about dismissal of all notifications.

Use this if your listener has a user interface that allows the user to dismiss all notifications, similar to the behavior of Android's status bar and notification panel. It should be called after the user invokes the "dismiss all" function of your UI; upon being informed, the notification manager will actually remove all active notifications and you will get multiple onNotificationRemoved(StatusBarNotification) callbacks.

The service should wait for the onListenerConnected() event before performing this operation. {@see #cancelNotification(String, String, int)}

/** * Inform the notification manager about dismissal of all notifications. * <p> * Use this if your listener has a user interface that allows the user to dismiss all * notifications, similar to the behavior of Android's status bar and notification panel. * It should be called after the user invokes the "dismiss all" function of your UI; * upon being informed, the notification manager will actually remove all active notifications * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * {@see #cancelNotification(String, String, int)} */
public final void cancelAllNotifications() { cancelNotifications(null /*all*/); }
Inform the notification manager about dismissal of specific notifications.

Use this if your listener has a user interface that allows the user to dismiss multiple notifications at once.

The service should wait for the onListenerConnected() event before performing this operation.

Params:
  • keys – Notifications to dismiss, or null to dismiss all. {@see #cancelNotification(String, String, int)}
/** * Inform the notification manager about dismissal of specific notifications. * <p> * Use this if your listener has a user interface that allows the user to dismiss * multiple notifications at once. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @param keys Notifications to dismiss, or {@code null} to dismiss all. * * {@see #cancelNotification(String, String, int)} */
public final void cancelNotifications(String[] keys) { if (!isBound()) return; try { getNotificationInterface().cancelNotificationsFromListener(mWrapper, keys); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } }
Inform the notification manager about snoozing a specific notification.

Use this if your listener has a user interface that allows the user to snooze a notification until a given SnoozeCriterion. It should be called after the user snoozes a single notification using your UI; upon being informed, the notification manager will actually remove the notification and you will get an onNotificationRemoved(StatusBarNotification) callback. When the snoozing period expires, you will get a onNotificationPosted(StatusBarNotification, RankingMap) callback for the notification.

Params:
  • key – The key of the notification to snooze
  • snoozeCriterionId – TheSnoozeCriterion.getId() of a context to snooze the notification until.
@hide
@removed
/** * Inform the notification manager about snoozing a specific notification. * <p> * Use this if your listener has a user interface that allows the user to snooze a notification * until a given {@link SnoozeCriterion}. It should be called after the user snoozes a single * notification using your UI; upon being informed, the notification manager will actually * remove the notification and you will get an * {@link #onNotificationRemoved(StatusBarNotification)} callback. When the snoozing period * expires, you will get a {@link #onNotificationPosted(StatusBarNotification, RankingMap)} * callback for the notification. * @param key The key of the notification to snooze * @param snoozeCriterionId The{@link SnoozeCriterion#getId()} of a context to snooze the * notification until. * @hide * @removed */
@SystemApi public final void snoozeNotification(String key, String snoozeCriterionId) { if (!isBound()) return; try { getNotificationInterface().snoozeNotificationUntilContextFromListener( mWrapper, key, snoozeCriterionId); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } }
Inform the notification manager about snoozing a specific notification.

Use this if your listener has a user interface that allows the user to snooze a notification for a time. It should be called after the user snoozes a single notification using your UI; upon being informed, the notification manager will actually remove the notification and you will get an onNotificationRemoved(StatusBarNotification) callback. When the snoozing period expires, you will get a onNotificationPosted(StatusBarNotification, RankingMap) callback for the notification.

Params:
  • key – The key of the notification to snooze
  • durationMs – A duration to snooze the notification for, in milliseconds.
/** * Inform the notification manager about snoozing a specific notification. * <p> * Use this if your listener has a user interface that allows the user to snooze a notification * for a time. It should be called after the user snoozes a single notification using * your UI; upon being informed, the notification manager will actually remove the notification * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. When the * snoozing period expires, you will get a * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the * notification. * @param key The key of the notification to snooze * @param durationMs A duration to snooze the notification for, in milliseconds. */
public final void snoozeNotification(String key, long durationMs) { if (!isBound()) return; try { getNotificationInterface().snoozeNotificationUntilFromListener( mWrapper, key, durationMs); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } }
Inform the notification manager that these notifications have been viewed by the user. This should only be called when there is sufficient confidence that the user is looking at the notifications, such as when the notifications appear on the screen due to an explicit user interaction.

The service should wait for the onListenerConnected() event before performing this operation.

Params:
  • keys – Notifications to mark as seen.
/** * Inform the notification manager that these notifications have been viewed by the * user. This should only be called when there is sufficient confidence that the user is * looking at the notifications, such as when the notifications appear on the screen due to * an explicit user interaction. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @param keys Notifications to mark as seen. */
public final void setNotificationsShown(String[] keys) { if (!isBound()) return; try { getNotificationInterface().setNotificationsShownFromListener(mWrapper, keys); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } }
Updates a notification channel for a given package for a given user. This should only be used to reflect changes a user has made to the channel via the listener's user interface.

This method will throw a security exception if you don't have access to notifications for the given user.

The caller must have an associated device in order to use this method.

Params:
  • pkg – The package the channel belongs to.
  • user – The user the channel belongs to.
  • channel – the channel to update.
/** * Updates a notification channel for a given package for a given user. This should only be used * to reflect changes a user has made to the channel via the listener's user interface. * * <p>This method will throw a security exception if you don't have access to notifications * for the given user.</p> * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated * device} in order to use this method. * * @param pkg The package the channel belongs to. * @param user The user the channel belongs to. * @param channel the channel to update. */
public final void updateNotificationChannel(@NonNull String pkg, @NonNull UserHandle user, @NonNull NotificationChannel channel) { if (!isBound()) return; try { getNotificationInterface().updateNotificationChannelFromPrivilegedListener( mWrapper, pkg, user, channel); } catch (RemoteException e) { Log.v(TAG, "Unable to contact notification manager", e); throw e.rethrowFromSystemServer(); } }
Returns all notification channels belonging to the given package for a given user.

This method will throw a security exception if you don't have access to notifications for the given user.

The caller must have an associated device in order to use this method.

Params:
  • pkg – The package to retrieve channels for.
/** * Returns all notification channels belonging to the given package for a given user. * * <p>This method will throw a security exception if you don't have access to notifications * for the given user.</p> * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated * device} in order to use this method. * * @param pkg The package to retrieve channels for. */
public final List<NotificationChannel> getNotificationChannels(@NonNull String pkg, @NonNull UserHandle user) { if (!isBound()) return null; try { return getNotificationInterface().getNotificationChannelsFromPrivilegedListener( mWrapper, pkg, user).getList(); } catch (RemoteException e) { Log.v(TAG, "Unable to contact notification manager", e); throw e.rethrowFromSystemServer(); } }
Returns all notification channel groups belonging to the given package for a given user.

This method will throw a security exception if you don't have access to notifications for the given user.

The caller must have an associated device in order to use this method.

Params:
  • pkg – The package to retrieve channel groups for.
/** * Returns all notification channel groups belonging to the given package for a given user. * * <p>This method will throw a security exception if you don't have access to notifications * for the given user.</p> * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated * device} in order to use this method. * * @param pkg The package to retrieve channel groups for. */
public final List<NotificationChannelGroup> getNotificationChannelGroups(@NonNull String pkg, @NonNull UserHandle user) { if (!isBound()) return null; try { return getNotificationInterface().getNotificationChannelGroupsFromPrivilegedListener( mWrapper, pkg, user).getList(); } catch (RemoteException e) { Log.v(TAG, "Unable to contact notification manager", e); throw e.rethrowFromSystemServer(); } }
Sets the notification trim that will be received via onNotificationPosted.

Setting a trim other than TRIM_FULL enables listeners that don't need access to the full notification features right away to reduce their memory footprint. Full notifications can be requested on-demand via getActiveNotifications(int).

Set to TRIM_FULL initially.

The service should wait for the onListenerConnected() event before performing this operation.

Params:
@hide
@removed
/** * Sets the notification trim that will be received via {@link #onNotificationPosted}. * * <p> * Setting a trim other than {@link #TRIM_FULL} enables listeners that don't need access to the * full notification features right away to reduce their memory footprint. Full notifications * can be requested on-demand via {@link #getActiveNotifications(int)}. * * <p> * Set to {@link #TRIM_FULL} initially. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @hide * @removed * * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}. * See <code>TRIM_*</code> constants. */
@SystemApi public final void setOnNotificationPostedTrim(int trim) { if (!isBound()) return; try { getNotificationInterface().setOnNotificationPostedTrimFromListener(mWrapper, trim); } catch (RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } }
Request the list of outstanding notifications (that is, those that are visible to the current user). Useful when you don't know what's already been posted.

The service should wait for the onListenerConnected() event before performing this operation.

Returns:An array of active notifications, sorted in natural order.
/** * Request the list of outstanding notifications (that is, those that are visible to the * current user). Useful when you don't know what's already been posted. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @return An array of active notifications, sorted in natural order. */
public StatusBarNotification[] getActiveNotifications() { StatusBarNotification[] activeNotifications = getActiveNotifications(null, TRIM_FULL); return activeNotifications != null ? activeNotifications : new StatusBarNotification[0]; }
Like getActiveNotifications(), but returns the list of currently snoozed notifications, for all users this listener has access to.

The service should wait for the onListenerConnected() event before performing this operation.

Returns:An array of snoozed notifications, sorted in natural order.
/** * Like {@link #getActiveNotifications()}, but returns the list of currently snoozed * notifications, for all users this listener has access to. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @return An array of snoozed notifications, sorted in natural order. */
public final StatusBarNotification[] getSnoozedNotifications() { try { ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface() .getSnoozedNotificationsFromListener(mWrapper, TRIM_FULL); return cleanUpNotificationList(parceledList); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } return null; }
Request the list of outstanding notifications (that is, those that are visible to the current user). Useful when you don't know what's already been posted.
Params:
  • trim – trim of the notifications to be returned. See TRIM_* constants.
@hide
@removed
Returns:An array of active notifications, sorted in natural order.
/** * Request the list of outstanding notifications (that is, those that are visible to the * current user). Useful when you don't know what's already been posted. * * @hide * @removed * * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants. * @return An array of active notifications, sorted in natural order. */
@SystemApi public StatusBarNotification[] getActiveNotifications(int trim) { StatusBarNotification[] activeNotifications = getActiveNotifications(null, trim); return activeNotifications != null ? activeNotifications : new StatusBarNotification[0]; }
Request one or more notifications by key. Useful if you have been keeping track of notifications but didn't want to retain the bits, and now need to go back and extract more data out of those notifications.

The service should wait for the onListenerConnected() event before performing this operation.

Params:
  • keys – the keys of the notifications to request
Returns:An array of notifications corresponding to the requested keys, in the same order as the key list.
/** * Request one or more notifications by key. Useful if you have been keeping track of * notifications but didn't want to retain the bits, and now need to go back and extract * more data out of those notifications. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @param keys the keys of the notifications to request * @return An array of notifications corresponding to the requested keys, in the * same order as the key list. */
public StatusBarNotification[] getActiveNotifications(String[] keys) { StatusBarNotification[] activeNotifications = getActiveNotifications(keys, TRIM_FULL); return activeNotifications != null ? activeNotifications : new StatusBarNotification[0]; }
Request one or more notifications by key. Useful if you have been keeping track of notifications but didn't want to retain the bits, and now need to go back and extract more data out of those notifications.
Params:
  • keys – the keys of the notifications to request
  • trim – trim of the notifications to be returned. See TRIM_* constants.
@hide
@removed
Returns:An array of notifications corresponding to the requested keys, in the same order as the key list.
/** * Request one or more notifications by key. Useful if you have been keeping track of * notifications but didn't want to retain the bits, and now need to go back and extract * more data out of those notifications. * * @hide * @removed * * @param keys the keys of the notifications to request * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants. * @return An array of notifications corresponding to the requested keys, in the * same order as the key list. */
@SystemApi public StatusBarNotification[] getActiveNotifications(String[] keys, int trim) { if (!isBound()) return null; try { ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface() .getActiveNotificationsFromListener(mWrapper, keys, trim); return cleanUpNotificationList(parceledList); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } return null; } private StatusBarNotification[] cleanUpNotificationList( ParceledListSlice<StatusBarNotification> parceledList) { if (parceledList == null || parceledList.getList() == null) { return new StatusBarNotification[0]; } List<StatusBarNotification> list = parceledList.getList(); ArrayList<StatusBarNotification> corruptNotifications = null; int N = list.size(); for (int i = 0; i < N; i++) { StatusBarNotification sbn = list.get(i); Notification notification = sbn.getNotification(); try { // convert icon metadata to legacy format for older clients createLegacyIconExtras(notification); // populate remote views for older clients. maybePopulateRemoteViews(notification); // populate people for older clients. maybePopulatePeople(notification); } catch (IllegalArgumentException e) { if (corruptNotifications == null) { corruptNotifications = new ArrayList<>(N); } corruptNotifications.add(sbn); Log.w(TAG, "get(Active/Snoozed)Notifications: can't rebuild notification from " + sbn.getPackageName()); } } if (corruptNotifications != null) { list.removeAll(corruptNotifications); } return list.toArray(new StatusBarNotification[list.size()]); }
Gets the set of hints representing current state.

The current state may differ from the requested state if the hint represents state shared across all listeners or a feature the notification host does not support or refuses to grant.

The service should wait for the onListenerConnected() event before performing this operation.

Returns:Zero or more of the HINT_ constants.
/** * Gets the set of hints representing current state. * * <p> * The current state may differ from the requested state if the hint represents state * shared across all listeners or a feature the notification host does not support or refuses * to grant. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @return Zero or more of the HINT_ constants. */
public final int getCurrentListenerHints() { if (!isBound()) return 0; try { return getNotificationInterface().getHintsFromListener(mWrapper); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); return 0; } }
Gets the current notification interruption filter active on the host.

The interruption filter defines which notifications are allowed to interrupt the user (e.g. via sound & vibration) and is applied globally. Listeners can find out whether a specific notification matched the interruption filter via Ranking.matchesInterruptionFilter().

The current filter may differ from the previously requested filter if the notification host does not support or refuses to apply the requested filter, or if another component changed the filter in the meantime.

Listen for updates using onInterruptionFilterChanged(int).

The service should wait for the onListenerConnected() event before performing this operation.

Returns:One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when unavailable.
/** * Gets the current notification interruption filter active on the host. * * <p> * The interruption filter defines which notifications are allowed to interrupt the user * (e.g. via sound &amp; vibration) and is applied globally. Listeners can find out whether * a specific notification matched the interruption filter via * {@link Ranking#matchesInterruptionFilter()}. * <p> * The current filter may differ from the previously requested filter if the notification host * does not support or refuses to apply the requested filter, or if another component changed * the filter in the meantime. * <p> * Listen for updates using {@link #onInterruptionFilterChanged(int)}. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @return One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when * unavailable. */
public final int getCurrentInterruptionFilter() { if (!isBound()) return INTERRUPTION_FILTER_UNKNOWN; try { return getNotificationInterface().getInterruptionFilterFromListener(mWrapper); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); return INTERRUPTION_FILTER_UNKNOWN; } }
Sets the desired listener hints.

This is merely a request, the host may or may not choose to take action depending on other listener requests or other global state.

Listen for updates using onListenerHintsChanged(int).

The service should wait for the onListenerConnected() event before performing this operation.

Params:
  • hints – One or more of the HINT_ constants.
/** * Sets the desired {@link #getCurrentListenerHints() listener hints}. * * <p> * This is merely a request, the host may or may not choose to take action depending * on other listener requests or other global state. * <p> * Listen for updates using {@link #onListenerHintsChanged(int)}. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @param hints One or more of the HINT_ constants. */
public final void requestListenerHints(int hints) { if (!isBound()) return; try { getNotificationInterface().requestHintsFromListener(mWrapper, hints); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } }
Sets the desired interruption filter.

This is merely a request, the host may or may not choose to apply the requested interruption filter depending on other listener requests or other global state.

Listen for updates using onInterruptionFilterChanged(int).

The service should wait for the onListenerConnected() event before performing this operation.

Params:
  • interruptionFilter – One of the INTERRUPTION_FILTER_ constants.
/** * Sets the desired {@link #getCurrentInterruptionFilter() interruption filter}. * * <p> * This is merely a request, the host may or may not choose to apply the requested * interruption filter depending on other listener requests or other global state. * <p> * Listen for updates using {@link #onInterruptionFilterChanged(int)}. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants. */
public final void requestInterruptionFilter(int interruptionFilter) { if (!isBound()) return; try { getNotificationInterface() .requestInterruptionFilterFromListener(mWrapper, interruptionFilter); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } }
Returns current ranking information.

The returned object represents the current ranking snapshot and only applies for currently active notifications.

Generally you should use the RankingMap that is passed with events such as onNotificationPosted(StatusBarNotification, RankingMap), onNotificationRemoved(StatusBarNotification, RankingMap), and so on. This method should only be used when needing access outside of such events, for example to retrieve the RankingMap right after initialization.

The service should wait for the onListenerConnected() event before performing this operation.

Returns:A RankingMap object providing access to ranking information
/** * Returns current ranking information. * * <p> * The returned object represents the current ranking snapshot and only * applies for currently active notifications. * <p> * Generally you should use the RankingMap that is passed with events such * as {@link #onNotificationPosted(StatusBarNotification, RankingMap)}, * {@link #onNotificationRemoved(StatusBarNotification, RankingMap)}, and * so on. This method should only be used when needing access outside of * such events, for example to retrieve the RankingMap right after * initialization. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. * * @return A {@link RankingMap} object providing access to ranking information */
public RankingMap getCurrentRanking() { synchronized (mLock) { return mRankingMap; } }
This is not the lifecycle event you are looking for.

The service should wait for the onListenerConnected() event before performing any operations.

/** * This is not the lifecycle event you are looking for. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing any operations. */
@Override public IBinder onBind(Intent intent) { if (mWrapper == null) { mWrapper = new NotificationListenerWrapper(); } return mWrapper; }
@hide
/** @hide */
protected boolean isBound() { if (mWrapper == null) { Log.w(TAG, "Notification listener service not yet bound."); return false; } return true; } @Override public void onDestroy() { onListenerDisconnected(); super.onDestroy(); }
Directly register this service with the Notification Manager.

Only system services may use this call. It will fail for non-system callers. Apps should ask the user to add their listener in Settings.

Params:
  • context – Context required for accessing resources. Since this service isn't launched as a real Service when using this method, a context has to be passed in.
  • componentName – the component that will consume the notification information
  • currentUser – the user to use as the stream filter
@hide
@removed
/** * Directly register this service with the Notification Manager. * * <p>Only system services may use this call. It will fail for non-system callers. * Apps should ask the user to add their listener in Settings. * * @param context Context required for accessing resources. Since this service isn't * launched as a real Service when using this method, a context has to be passed in. * @param componentName the component that will consume the notification information * @param currentUser the user to use as the stream filter * @hide * @removed */
@SystemApi public void registerAsSystemService(Context context, ComponentName componentName, int currentUser) throws RemoteException { if (mWrapper == null) { mWrapper = new NotificationListenerWrapper(); } mSystemContext = context; INotificationManager noMan = getNotificationInterface(); mHandler = new MyHandler(context.getMainLooper()); mCurrentUser = currentUser; noMan.registerListener(mWrapper, componentName, currentUser); }
Directly unregister this service from the Notification Manager.

This method will fail for listeners that were not registered with (@link registerAsService).

@hide
@removed
/** * Directly unregister this service from the Notification Manager. * * <p>This method will fail for listeners that were not registered * with (@link registerAsService). * @hide * @removed */
@SystemApi public void unregisterAsSystemService() throws RemoteException { if (mWrapper != null) { INotificationManager noMan = getNotificationInterface(); noMan.unregisterListener(mWrapper, mCurrentUser); } }
Request that the listener be rebound, after a previous call to requestUnbind.

This method will fail for listeners that have not been granted the permission by the user.

/** * Request that the listener be rebound, after a previous call to {@link #requestUnbind}. * * <p>This method will fail for listeners that have * not been granted the permission by the user. */
public static void requestRebind(ComponentName componentName) { INotificationManager noMan = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); try { noMan.requestBindListener(componentName); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } }
Request that the service be unbound.

Once this is called, you will no longer receive updates and no method calls are guaranteed to be successful, until you next receive the onListenerConnected() event. The service will likely be killed by the system after this call.

The service should wait for the onListenerConnected() event before performing this operation. I know it's tempting, but you must wait.

/** * Request that the service be unbound. * * <p>Once this is called, you will no longer receive updates and no method calls are * guaranteed to be successful, until you next receive the {@link #onListenerConnected()} event. * The service will likely be killed by the system after this call. * * <p>The service should wait for the {@link #onListenerConnected()} event * before performing this operation. I know it's tempting, but you must wait. */
public final void requestUnbind() { if (mWrapper != null) { INotificationManager noMan = getNotificationInterface(); try { noMan.requestUnbindListener(mWrapper); // Disable future messages. isConnected = false; } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } }
Convert new-style Icons to legacy representations for pre-M clients.
/** Convert new-style Icons to legacy representations for pre-M clients. */
private void createLegacyIconExtras(Notification n) { Icon smallIcon = n.getSmallIcon(); Icon largeIcon = n.getLargeIcon(); if (smallIcon != null && smallIcon.getType() == Icon.TYPE_RESOURCE) { n.extras.putInt(Notification.EXTRA_SMALL_ICON, smallIcon.getResId()); n.icon = smallIcon.getResId(); } if (largeIcon != null) { Drawable d = largeIcon.loadDrawable(getContext()); if (d != null && d instanceof BitmapDrawable) { final Bitmap largeIconBits = ((BitmapDrawable) d).getBitmap(); n.extras.putParcelable(Notification.EXTRA_LARGE_ICON, largeIconBits); n.largeIcon = largeIconBits; } } }
Populates remote views for pre-N targeting apps.
/** * Populates remote views for pre-N targeting apps. */
private void maybePopulateRemoteViews(Notification notification) { if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) { Builder builder = Builder.recoverBuilder(getContext(), notification); // Some styles wrap Notification's contentView, bigContentView and headsUpContentView. // First inflate them all, only then set them to avoid recursive wrapping. RemoteViews content = builder.createContentView(); RemoteViews big = builder.createBigContentView(); RemoteViews headsUp = builder.createHeadsUpContentView(); notification.contentView = content; notification.bigContentView = big; notification.headsUpContentView = headsUp; } }
Populates remote views for pre-P targeting apps.
/** * Populates remote views for pre-P targeting apps. */
private void maybePopulatePeople(Notification notification) { if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P) { ArrayList<Person> people = notification.extras.getParcelableArrayList( Notification.EXTRA_PEOPLE_LIST); if (people != null && people.isEmpty()) { int size = people.size(); String[] peopleArray = new String[size]; for (int i = 0; i < size; i++) { Person person = people.get(i); peopleArray[i] = person.resolveToLegacyUri(); } notification.extras.putStringArray(Notification.EXTRA_PEOPLE, peopleArray); } } }
@hide
/** @hide */
protected class NotificationListenerWrapper extends INotificationListener.Stub { @Override public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder, NotificationRankingUpdate update) { StatusBarNotification sbn; try { sbn = sbnHolder.get(); } catch (RemoteException e) { Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e); return; } try { // convert icon metadata to legacy format for older clients createLegacyIconExtras(sbn.getNotification()); maybePopulateRemoteViews(sbn.getNotification()); maybePopulatePeople(sbn.getNotification()); } catch (IllegalArgumentException e) { // warn and drop corrupt notification Log.w(TAG, "onNotificationPosted: can't rebuild notification from " + sbn.getPackageName()); sbn = null; } // protect subclass from concurrent modifications of (@link mNotificationKeys}. synchronized (mLock) { applyUpdateLocked(update); if (sbn != null) { SomeArgs args = SomeArgs.obtain(); args.arg1 = sbn; args.arg2 = mRankingMap; mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED, args).sendToTarget(); } else { // still pass along the ranking map, it may contain other information mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE, mRankingMap).sendToTarget(); } } } @Override public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder, NotificationRankingUpdate update, NotificationStats stats, int reason) { StatusBarNotification sbn; try { sbn = sbnHolder.get(); } catch (RemoteException e) { Log.w(TAG, "onNotificationRemoved: Error receiving StatusBarNotification", e); return; } // protect subclass from concurrent modifications of (@link mNotificationKeys}. synchronized (mLock) { applyUpdateLocked(update); SomeArgs args = SomeArgs.obtain(); args.arg1 = sbn; args.arg2 = mRankingMap; args.arg3 = reason; args.arg4 = stats; mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED, args).sendToTarget(); } } @Override public void onListenerConnected(NotificationRankingUpdate update) { // protect subclass from concurrent modifications of (@link mNotificationKeys}. synchronized (mLock) { applyUpdateLocked(update); } isConnected = true; mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_CONNECTED).sendToTarget(); } @Override public void onNotificationRankingUpdate(NotificationRankingUpdate update) throws RemoteException { // protect subclass from concurrent modifications of (@link mNotificationKeys}. synchronized (mLock) { applyUpdateLocked(update); mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE, mRankingMap).sendToTarget(); } } @Override public void onListenerHintsChanged(int hints) throws RemoteException { mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_HINTS_CHANGED, hints, 0).sendToTarget(); } @Override public void onInterruptionFilterChanged(int interruptionFilter) throws RemoteException { mHandler.obtainMessage(MyHandler.MSG_ON_INTERRUPTION_FILTER_CHANGED, interruptionFilter, 0).sendToTarget(); } @Override public void onNotificationEnqueued(IStatusBarNotificationHolder notificationHolder) throws RemoteException { // no-op in the listener } @Override public void onNotificationSnoozedUntilContext( IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId) throws RemoteException { // no-op in the listener } @Override public void onNotificationChannelModification(String pkgName, UserHandle user, NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType) { SomeArgs args = SomeArgs.obtain(); args.arg1 = pkgName; args.arg2 = user; args.arg3 = channel; args.arg4 = modificationType; mHandler.obtainMessage( MyHandler.MSG_ON_NOTIFICATION_CHANNEL_MODIFIED, args).sendToTarget(); } @Override public void onNotificationChannelGroupModification(String pkgName, UserHandle user, NotificationChannelGroup group, @ChannelOrGroupModificationTypes int modificationType) { SomeArgs args = SomeArgs.obtain(); args.arg1 = pkgName; args.arg2 = user; args.arg3 = group; args.arg4 = modificationType; mHandler.obtainMessage( MyHandler.MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED, args).sendToTarget(); } }
@hide
/** * @hide */
@GuardedBy("mLock") public final void applyUpdateLocked(NotificationRankingUpdate update) { mRankingMap = new RankingMap(update); }
@hide
/** @hide */
protected Context getContext() { if (mSystemContext != null) { return mSystemContext; } return this; }
Stores ranking related information on a currently active notification.

Ranking objects aren't automatically updated as notification events occur. Instead, ranking information has to be retrieved again via the current RankingMap.

/** * Stores ranking related information on a currently active notification. * * <p> * Ranking objects aren't automatically updated as notification events * occur. Instead, ranking information has to be retrieved again via the * current {@link RankingMap}. */
public static class Ranking {
Value signifying that the user has not expressed a per-app visibility override value.
@hide
/** Value signifying that the user has not expressed a per-app visibility override value. * @hide */
public static final int VISIBILITY_NO_OVERRIDE = NotificationManager.VISIBILITY_NO_OVERRIDE;
The user is likely to have a negative reaction to this notification.
/** * The user is likely to have a negative reaction to this notification. */
public static final int USER_SENTIMENT_NEGATIVE = -1;
It is not known how the user will react to this notification.
/** * It is not known how the user will react to this notification. */
public static final int USER_SENTIMENT_NEUTRAL = 0;
The user is likely to have a positive reaction to this notification.
/** * The user is likely to have a positive reaction to this notification. */
public static final int USER_SENTIMENT_POSITIVE = 1;
@hide
/** @hide */
@IntDef(prefix = { "USER_SENTIMENT_" }, value = { USER_SENTIMENT_NEGATIVE, USER_SENTIMENT_NEUTRAL, USER_SENTIMENT_POSITIVE }) @Retention(RetentionPolicy.SOURCE) public @interface UserSentiment {} private String mKey; private int mRank = -1; private boolean mIsAmbient; private boolean mMatchesInterruptionFilter; private int mVisibilityOverride; private int mSuppressedVisualEffects; private @NotificationManager.Importance int mImportance; private CharSequence mImportanceExplanation; // System specified group key. private String mOverrideGroupKey; // Notification assistant channel override. private NotificationChannel mChannel; // Notification assistant people override. private ArrayList<String> mOverridePeople; // Notification assistant snooze criteria. private ArrayList<SnoozeCriterion> mSnoozeCriteria; private boolean mShowBadge; private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL; private boolean mHidden; public Ranking() {}
Returns the key of the notification this Ranking applies to.
/** * Returns the key of the notification this Ranking applies to. */
public String getKey() { return mKey; }
Returns the rank of the notification.
Returns:the rank of the notification, that is the 0-based index in the list of active notifications.
/** * Returns the rank of the notification. * * @return the rank of the notification, that is the 0-based index in * the list of active notifications. */
public int getRank() { return mRank; }
Returns whether the notification is an ambient notification, that is a notification that doesn't require the user's immediate attention.
/** * Returns whether the notification is an ambient notification, that is * a notification that doesn't require the user's immediate attention. */
public boolean isAmbient() { return mIsAmbient; }
Returns the user specified visibility for the package that posted this notification, or VISIBILITY_NO_OVERRIDE if no such preference has been expressed.
@hide
/** * Returns the user specified visibility for the package that posted * this notification, or * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if * no such preference has been expressed. * @hide */
public int getVisibilityOverride() { return mVisibilityOverride; }
Returns the type(s) of visual effects that should be suppressed for this notification. See Policy, e.g. Policy.SUPPRESSED_EFFECT_LIGHTS.
/** * Returns the type(s) of visual effects that should be suppressed for this notification. * See {@link NotificationManager.Policy}, e.g. * {@link NotificationManager.Policy#SUPPRESSED_EFFECT_LIGHTS}. */
public int getSuppressedVisualEffects() { return mSuppressedVisualEffects; }
Returns whether the notification matches the user's interruption filter.
Returns:true if the notification is allowed by the filter, or false if it is blocked.
/** * Returns whether the notification matches the user's interruption * filter. * * @return {@code true} if the notification is allowed by the filter, or * {@code false} if it is blocked. */
public boolean matchesInterruptionFilter() { return mMatchesInterruptionFilter; }
Returns the importance of the notification, which dictates its modes of presentation, see: NotificationManager.IMPORTANCE_DEFAULT, etc.
Returns:the importance of the notification
/** * Returns the importance of the notification, which dictates its * modes of presentation, see: {@link NotificationManager#IMPORTANCE_DEFAULT}, etc. * * @return the importance of the notification */
public @NotificationManager.Importance int getImportance() { return mImportance; }
If the importance has been overridden by user preference, then this will be non-null, and should be displayed to the user.
Returns:the explanation for the importance, or null if it is the natural importance
/** * If the importance has been overridden by user preference, then this will be non-null, * and should be displayed to the user. * * @return the explanation for the importance, or null if it is the natural importance */
public CharSequence getImportanceExplanation() { return mImportanceExplanation; }
If the system has overridden the group key, then this will be non-null, and this key should be used to bundle notifications.
/** * If the system has overridden the group key, then this will be non-null, and this * key should be used to bundle notifications. */
public String getOverrideGroupKey() { return mOverrideGroupKey; }
Returns the notification channel this notification was posted to, which dictates notification behavior and presentation.
/** * Returns the notification channel this notification was posted to, which dictates * notification behavior and presentation. */
public NotificationChannel getChannel() { return mChannel; }
Returns how the system thinks the user feels about notifications from the channel provided by getChannel(). You can use this information to expose controls to help the user block this channel's notifications, if the sentiment is USER_SENTIMENT_NEGATIVE, or emphasize this notification if the sentiment is USER_SENTIMENT_POSITIVE.
/** * Returns how the system thinks the user feels about notifications from the * channel provided by {@link #getChannel()}. You can use this information to expose * controls to help the user block this channel's notifications, if the sentiment is * {@link #USER_SENTIMENT_NEGATIVE}, or emphasize this notification if the sentiment is * {@link #USER_SENTIMENT_POSITIVE}. */
public int getUserSentiment() { return mUserSentiment; }
If the NotificationAssistantService has added people to this notification, then this will be non-null.
@hide
@removed
/** * If the {@link NotificationAssistantService} has added people to this notification, then * this will be non-null. * @hide * @removed */
@SystemApi public List<String> getAdditionalPeople() { return mOverridePeople; }
Returns snooze criteria provided by the NotificationAssistantService. If your user interface displays options for snoozing notifications these criteria should be displayed as well.
@hide
@removed
/** * Returns snooze criteria provided by the {@link NotificationAssistantService}. If your * user interface displays options for snoozing notifications these criteria should be * displayed as well. * @hide * @removed */
@SystemApi public List<SnoozeCriterion> getSnoozeCriteria() { return mSnoozeCriteria; }
Returns whether this notification can be displayed as a badge.
Returns:true if the notification can be displayed as a badge, false otherwise.
/** * Returns whether this notification can be displayed as a badge. * * @return true if the notification can be displayed as a badge, false otherwise. */
public boolean canShowBadge() { return mShowBadge; }
Returns whether the app that posted this notification is suspended, so this notification should be hidden.
Returns:true if the notification should be hidden, false otherwise.
/** * Returns whether the app that posted this notification is suspended, so this notification * should be hidden. * * @return true if the notification should be hidden, false otherwise. */
public boolean isSuspended() { return mHidden; }
@hide
/** * @hide */
@VisibleForTesting public void populate(String key, int rank, boolean matchesInterruptionFilter, int visibilityOverride, int suppressedVisualEffects, int importance, CharSequence explanation, String overrideGroupKey, NotificationChannel channel, ArrayList<String> overridePeople, ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge, int userSentiment, boolean hidden) { mKey = key; mRank = rank; mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW; mMatchesInterruptionFilter = matchesInterruptionFilter; mVisibilityOverride = visibilityOverride; mSuppressedVisualEffects = suppressedVisualEffects; mImportance = importance; mImportanceExplanation = explanation; mOverrideGroupKey = overrideGroupKey; mChannel = channel; mOverridePeople = overridePeople; mSnoozeCriteria = snoozeCriteria; mShowBadge = showBadge; mUserSentiment = userSentiment; mHidden = hidden; }
{@hide}
/** * {@hide} */
public static String importanceToString(int importance) { switch (importance) { case NotificationManager.IMPORTANCE_UNSPECIFIED: return "UNSPECIFIED"; case NotificationManager.IMPORTANCE_NONE: return "NONE"; case NotificationManager.IMPORTANCE_MIN: return "MIN"; case NotificationManager.IMPORTANCE_LOW: return "LOW"; case NotificationManager.IMPORTANCE_DEFAULT: return "DEFAULT"; case NotificationManager.IMPORTANCE_HIGH: case NotificationManager.IMPORTANCE_MAX: return "HIGH"; default: return "UNKNOWN(" + String.valueOf(importance) + ")"; } } }
Provides access to ranking information on currently active notifications.

Note that this object represents a ranking snapshot that only applies to notifications active at the time of retrieval.

/** * Provides access to ranking information on currently active * notifications. * * <p> * Note that this object represents a ranking snapshot that only applies to * notifications active at the time of retrieval. */
public static class RankingMap implements Parcelable { private final NotificationRankingUpdate mRankingUpdate; private ArrayMap<String,Integer> mRanks; private ArraySet<Object> mIntercepted; private ArrayMap<String, Integer> mVisibilityOverrides; private ArrayMap<String, Integer> mSuppressedVisualEffects; private ArrayMap<String, Integer> mImportance; private ArrayMap<String, String> mImportanceExplanation; private ArrayMap<String, String> mOverrideGroupKeys; private ArrayMap<String, NotificationChannel> mChannels; private ArrayMap<String, ArrayList<String>> mOverridePeople; private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria; private ArrayMap<String, Boolean> mShowBadge; private ArrayMap<String, Integer> mUserSentiment; private ArrayMap<String, Boolean> mHidden; private RankingMap(NotificationRankingUpdate rankingUpdate) { mRankingUpdate = rankingUpdate; }
Request the list of notification keys in their current ranking order.
Returns:An array of active notification keys, in their ranking order.
/** * Request the list of notification keys in their current ranking * order. * * @return An array of active notification keys, in their ranking order. */
public String[] getOrderedKeys() { return mRankingUpdate.getOrderedKeys(); }
Populates outRanking with ranking information for the notification with the given key.
Returns:true if a valid key has been passed and outRanking has been populated; false otherwise
/** * Populates outRanking with ranking information for the notification * with the given key. * * @return true if a valid key has been passed and outRanking has * been populated; false otherwise */
public boolean getRanking(String key, Ranking outRanking) { int rank = getRank(key); outRanking.populate(key, rank, !isIntercepted(key), getVisibilityOverride(key), getSuppressedVisualEffects(key), getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key), getChannel(key), getOverridePeople(key), getSnoozeCriteria(key), getShowBadge(key), getUserSentiment(key), getHidden(key)); return rank >= 0; } private int getRank(String key) { synchronized (this) { if (mRanks == null) { buildRanksLocked(); } } Integer rank = mRanks.get(key); return rank != null ? rank : -1; } private boolean isIntercepted(String key) { synchronized (this) { if (mIntercepted == null) { buildInterceptedSetLocked(); } } return mIntercepted.contains(key); } private int getVisibilityOverride(String key) { synchronized (this) { if (mVisibilityOverrides == null) { buildVisibilityOverridesLocked(); } } Integer override = mVisibilityOverrides.get(key); if (override == null) { return Ranking.VISIBILITY_NO_OVERRIDE; } return override.intValue(); } private int getSuppressedVisualEffects(String key) { synchronized (this) { if (mSuppressedVisualEffects == null) { buildSuppressedVisualEffectsLocked(); } } Integer suppressed = mSuppressedVisualEffects.get(key); if (suppressed == null) { return 0; } return suppressed.intValue(); } private int getImportance(String key) { synchronized (this) { if (mImportance == null) { buildImportanceLocked(); } } Integer importance = mImportance.get(key); if (importance == null) { return NotificationManager.IMPORTANCE_DEFAULT; } return importance.intValue(); } private String getImportanceExplanation(String key) { synchronized (this) { if (mImportanceExplanation == null) { buildImportanceExplanationLocked(); } } return mImportanceExplanation.get(key); } private String getOverrideGroupKey(String key) { synchronized (this) { if (mOverrideGroupKeys == null) { buildOverrideGroupKeys(); } } return mOverrideGroupKeys.get(key); } private NotificationChannel getChannel(String key) { synchronized (this) { if (mChannels == null) { buildChannelsLocked(); } } return mChannels.get(key); } private ArrayList<String> getOverridePeople(String key) { synchronized (this) { if (mOverridePeople == null) { buildOverridePeopleLocked(); } } return mOverridePeople.get(key); } private ArrayList<SnoozeCriterion> getSnoozeCriteria(String key) { synchronized (this) { if (mSnoozeCriteria == null) { buildSnoozeCriteriaLocked(); } } return mSnoozeCriteria.get(key); } private boolean getShowBadge(String key) { synchronized (this) { if (mShowBadge == null) { buildShowBadgeLocked(); } } Boolean showBadge = mShowBadge.get(key); return showBadge == null ? false : showBadge.booleanValue(); } private int getUserSentiment(String key) { synchronized (this) { if (mUserSentiment == null) { buildUserSentimentLocked(); } } Integer userSentiment = mUserSentiment.get(key); return userSentiment == null ? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue(); } private boolean getHidden(String key) { synchronized (this) { if (mHidden == null) { buildHiddenLocked(); } } Boolean hidden = mHidden.get(key); return hidden == null ? false : hidden.booleanValue(); } // Locked by 'this' private void buildRanksLocked() { String[] orderedKeys = mRankingUpdate.getOrderedKeys(); mRanks = new ArrayMap<>(orderedKeys.length); for (int i = 0; i < orderedKeys.length; i++) { String key = orderedKeys[i]; mRanks.put(key, i); } } // Locked by 'this' private void buildInterceptedSetLocked() { String[] dndInterceptedKeys = mRankingUpdate.getInterceptedKeys(); mIntercepted = new ArraySet<>(dndInterceptedKeys.length); Collections.addAll(mIntercepted, dndInterceptedKeys); } // Locked by 'this' private void buildVisibilityOverridesLocked() { Bundle visibilityBundle = mRankingUpdate.getVisibilityOverrides(); mVisibilityOverrides = new ArrayMap<>(visibilityBundle.size()); for (String key: visibilityBundle.keySet()) { mVisibilityOverrides.put(key, visibilityBundle.getInt(key)); } } // Locked by 'this' private void buildSuppressedVisualEffectsLocked() { Bundle suppressedBundle = mRankingUpdate.getSuppressedVisualEffects(); mSuppressedVisualEffects = new ArrayMap<>(suppressedBundle.size()); for (String key: suppressedBundle.keySet()) { mSuppressedVisualEffects.put(key, suppressedBundle.getInt(key)); } } // Locked by 'this' private void buildImportanceLocked() { String[] orderedKeys = mRankingUpdate.getOrderedKeys(); int[] importance = mRankingUpdate.getImportance(); mImportance = new ArrayMap<>(orderedKeys.length); for (int i = 0; i < orderedKeys.length; i++) { String key = orderedKeys[i]; mImportance.put(key, importance[i]); } } // Locked by 'this' private void buildImportanceExplanationLocked() { Bundle explanationBundle = mRankingUpdate.getImportanceExplanation(); mImportanceExplanation = new ArrayMap<>(explanationBundle.size()); for (String key: explanationBundle.keySet()) { mImportanceExplanation.put(key, explanationBundle.getString(key)); } } // Locked by 'this' private void buildOverrideGroupKeys() { Bundle overrideGroupKeys = mRankingUpdate.getOverrideGroupKeys(); mOverrideGroupKeys = new ArrayMap<>(overrideGroupKeys.size()); for (String key: overrideGroupKeys.keySet()) { mOverrideGroupKeys.put(key, overrideGroupKeys.getString(key)); } } // Locked by 'this' private void buildChannelsLocked() { Bundle channels = mRankingUpdate.getChannels(); mChannels = new ArrayMap<>(channels.size()); for (String key : channels.keySet()) { mChannels.put(key, channels.getParcelable(key)); } } // Locked by 'this' private void buildOverridePeopleLocked() { Bundle overridePeople = mRankingUpdate.getOverridePeople(); mOverridePeople = new ArrayMap<>(overridePeople.size()); for (String key : overridePeople.keySet()) { mOverridePeople.put(key, overridePeople.getStringArrayList(key)); } } // Locked by 'this' private void buildSnoozeCriteriaLocked() { Bundle snoozeCriteria = mRankingUpdate.getSnoozeCriteria(); mSnoozeCriteria = new ArrayMap<>(snoozeCriteria.size()); for (String key : snoozeCriteria.keySet()) { mSnoozeCriteria.put(key, snoozeCriteria.getParcelableArrayList(key)); } } // Locked by 'this' private void buildShowBadgeLocked() { Bundle showBadge = mRankingUpdate.getShowBadge(); mShowBadge = new ArrayMap<>(showBadge.size()); for (String key : showBadge.keySet()) { mShowBadge.put(key, showBadge.getBoolean(key)); } } // Locked by 'this' private void buildUserSentimentLocked() { Bundle userSentiment = mRankingUpdate.getUserSentiment(); mUserSentiment = new ArrayMap<>(userSentiment.size()); for (String key : userSentiment.keySet()) { mUserSentiment.put(key, userSentiment.getInt(key)); } } // Locked by 'this' private void buildHiddenLocked() { Bundle hidden = mRankingUpdate.getHidden(); mHidden = new ArrayMap<>(hidden.size()); for (String key : hidden.keySet()) { mHidden.put(key, hidden.getBoolean(key)); } } // ----------- Parcelable @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(mRankingUpdate, flags); } public static final Creator<RankingMap> CREATOR = new Creator<RankingMap>() { @Override public RankingMap createFromParcel(Parcel source) { NotificationRankingUpdate rankingUpdate = source.readParcelable(null); return new RankingMap(rankingUpdate); } @Override public RankingMap[] newArray(int size) { return new RankingMap[size]; } }; } private final class MyHandler extends Handler { public static final int MSG_ON_NOTIFICATION_POSTED = 1; public static final int MSG_ON_NOTIFICATION_REMOVED = 2; public static final int MSG_ON_LISTENER_CONNECTED = 3; public static final int MSG_ON_NOTIFICATION_RANKING_UPDATE = 4; public static final int MSG_ON_LISTENER_HINTS_CHANGED = 5; public static final int MSG_ON_INTERRUPTION_FILTER_CHANGED = 6; public static final int MSG_ON_NOTIFICATION_CHANNEL_MODIFIED = 7; public static final int MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED = 8; public MyHandler(Looper looper) { super(looper, null, false); } @Override public void handleMessage(Message msg) { if (!isConnected) { return; } switch (msg.what) { case MSG_ON_NOTIFICATION_POSTED: { SomeArgs args = (SomeArgs) msg.obj; StatusBarNotification sbn = (StatusBarNotification) args.arg1; RankingMap rankingMap = (RankingMap) args.arg2; args.recycle(); onNotificationPosted(sbn, rankingMap); } break; case MSG_ON_NOTIFICATION_REMOVED: { SomeArgs args = (SomeArgs) msg.obj; StatusBarNotification sbn = (StatusBarNotification) args.arg1; RankingMap rankingMap = (RankingMap) args.arg2; int reason = (int) args.arg3; NotificationStats stats = (NotificationStats) args.arg4; args.recycle(); onNotificationRemoved(sbn, rankingMap, stats, reason); } break; case MSG_ON_LISTENER_CONNECTED: { onListenerConnected(); } break; case MSG_ON_NOTIFICATION_RANKING_UPDATE: { RankingMap rankingMap = (RankingMap) msg.obj; onNotificationRankingUpdate(rankingMap); } break; case MSG_ON_LISTENER_HINTS_CHANGED: { final int hints = msg.arg1; onListenerHintsChanged(hints); } break; case MSG_ON_INTERRUPTION_FILTER_CHANGED: { final int interruptionFilter = msg.arg1; onInterruptionFilterChanged(interruptionFilter); } break; case MSG_ON_NOTIFICATION_CHANNEL_MODIFIED: { SomeArgs args = (SomeArgs) msg.obj; String pkgName = (String) args.arg1; UserHandle user= (UserHandle) args.arg2; NotificationChannel channel = (NotificationChannel) args.arg3; int modificationType = (int) args.arg4; onNotificationChannelModified(pkgName, user, channel, modificationType); } break; case MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED: { SomeArgs args = (SomeArgs) msg.obj; String pkgName = (String) args.arg1; UserHandle user = (UserHandle) args.arg2; NotificationChannelGroup group = (NotificationChannelGroup) args.arg3; int modificationType = (int) args.arg4; onNotificationChannelGroupModified(pkgName, user, group, modificationType); } break; } } } }