blob: 23607ebe78fe0cbd0853e3165195fba344b6c996 [file] [log] [blame]
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.service.notification;
18
Julia Reynolds73ed76b2017-04-04 17:04:38 -040019import android.annotation.IntDef;
20import android.annotation.NonNull;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070021import android.annotation.SdkConstant;
Julia Reynolds7ca33072017-06-29 13:58:24 -040022import android.annotation.SystemApi;
Julia Reynolds503ed942017-10-04 16:04:56 -040023import android.annotation.TestApi;
Mathew Inwoode3807372018-08-10 09:51:03 +010024import android.annotation.UnsupportedAppUsage;
Julia Reynolds81499612017-10-11 14:43:40 -040025import android.app.ActivityManager;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070026import android.app.INotificationManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020027import android.app.Notification;
28import android.app.Notification.Builder;
Julia Reynolds7ca33072017-06-29 13:58:24 -040029import android.app.NotificationChannel;
30import android.app.NotificationChannelGroup;
John Spurlock80774932015-05-07 17:38:50 -040031import android.app.NotificationManager;
Selim Cinek9acd6732018-03-23 16:39:02 -070032import android.app.Person;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070033import android.app.Service;
Jeff Sharkey67f9d502017-08-05 13:49:13 -060034import android.companion.CompanionDeviceManager;
Chris Wren1941fc72014-05-14 15:20:51 -040035import android.content.ComponentName;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070036import android.content.Context;
37import android.content.Intent;
Christoph Studercee44ba2014-05-20 18:36:43 +020038import android.content.pm.ParceledListSlice;
Julia Reynolds7ca33072017-06-29 13:58:24 -040039import android.graphics.Bitmap;
Daniel Sandlerf5a78382015-05-15 23:59:36 -040040import android.graphics.drawable.BitmapDrawable;
41import android.graphics.drawable.Drawable;
42import android.graphics.drawable.Icon;
Julia Reynoldsd9228f12015-10-20 10:37:27 -040043import android.os.Build;
Chris Wren3ad4e3a2014-09-02 17:23:51 -040044import android.os.Bundle;
Julia Reynolds7ca33072017-06-29 13:58:24 -040045import android.os.Handler;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070046import android.os.IBinder;
Julia Reynolds7ca33072017-06-29 13:58:24 -040047import android.os.Looper;
48import android.os.Message;
Christoph Studer05ad4822014-05-16 14:16:03 +020049import android.os.Parcel;
50import android.os.Parcelable;
Chris Wrenf9536642014-04-17 10:01:54 -040051import android.os.RemoteException;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070052import android.os.ServiceManager;
Julia Reynoldsf27d6b22017-04-13 15:48:16 -040053import android.os.UserHandle;
Christoph Studerdda48f12014-07-29 23:13:16 +020054import android.util.ArrayMap;
55import android.util.ArraySet;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070056import android.util.Log;
Adrian Roos5081c0d2016-02-26 16:04:19 -080057import android.widget.RemoteViews;
Julia Reynolds7ca33072017-06-29 13:58:24 -040058
Svet Ganovb8f53ee2016-02-18 08:38:56 -080059import com.android.internal.annotations.GuardedBy;
Dan Sandler1d958f82018-01-09 21:10:26 -050060import com.android.internal.annotations.VisibleForTesting;
Svet Ganovb8f53ee2016-02-18 08:38:56 -080061import com.android.internal.os.SomeArgs;
Julia Reynolds22f02b32016-12-01 15:05:13 -050062
Julia Reynolds73ed76b2017-04-04 17:04:38 -040063import java.lang.annotation.Retention;
64import java.lang.annotation.RetentionPolicy;
Chris Wren24fb8942015-06-18 14:33:56 -040065import java.util.ArrayList;
Christoph Studerdda48f12014-07-29 23:13:16 +020066import java.util.Collections;
Christoph Studercee44ba2014-05-20 18:36:43 +020067import java.util.List;
68
Scott Main04667da2013-04-25 16:57:16 -070069/**
Christoph Studer05ad4822014-05-16 14:16:03 +020070 * A service that receives calls from the system when new notifications are
71 * posted or removed, or their ranking changed.
Scott Main04667da2013-04-25 16:57:16 -070072 * <p>To extend this class, you must declare the service in your manifest file with
Jeff Sharkey67f9d502017-08-05 13:49:13 -060073 * the {@link android.Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE} permission
Scott Main04667da2013-04-25 16:57:16 -070074 * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
75 * <pre>
76 * &lt;service android:name=".NotificationListener"
77 * android:label="&#64;string/service_name"
78 * android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
79 * &lt;intent-filter>
80 * &lt;action android:name="android.service.notification.NotificationListenerService" />
81 * &lt;/intent-filter>
82 * &lt;/service></pre>
Chris Wren5717bd62016-04-06 18:15:46 -040083 *
84 * <p>The service should wait for the {@link #onListenerConnected()} event
85 * before performing any operations. The {@link #requestRebind(ComponentName)}
86 * method is the <i>only</i> one that is safe to call before {@link #onListenerConnected()}
87 * or after {@link #onListenerDisconnected()}.
88 * </p>
Julia Reynolds81499612017-10-11 14:43:40 -040089 * <p> Notification listeners cannot get notification access or be bound by the system on
Benjamin Miller604843b2018-02-27 11:29:16 +000090 * {@linkplain ActivityManager#isLowRamDevice() low-RAM} devices. The system also ignores
91 * notification listeners running in a work profile. A
92 * {@link android.app.admin.DevicePolicyManager} might block notifications originating from a work
93 * profile.</p>
Julia Reynoldsfe992192018-09-07 10:33:19 -040094 * <p>
95 * From {@link Build.VERSION_CODES#N} onward all callbacks are called on the main thread. Prior
96 * to N, there is no guarantee on what thread the callback will happen.
97 * </p>
Scott Main04667da2013-04-25 16:57:16 -070098 */
Daniel Sandler5feceeb2013-03-22 18:29:23 -070099public abstract class NotificationListenerService extends Service {
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -0500100
Mathew Inwood31755f92018-12-20 13:53:36 +0000101 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Julia Reynolds7e1ffd72016-11-28 13:50:22 -0500102 private final String TAG = getClass().getSimpleName();
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700103
Christoph Studer85a384b2014-08-27 20:16:15 +0200104 /**
105 * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
106 * Normal interruption filter.
107 */
John Spurlock80774932015-05-07 17:38:50 -0400108 public static final int INTERRUPTION_FILTER_ALL
109 = NotificationManager.INTERRUPTION_FILTER_ALL;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400110
Christoph Studer85a384b2014-08-27 20:16:15 +0200111 /**
112 * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
113 * Priority interruption filter.
114 */
John Spurlock80774932015-05-07 17:38:50 -0400115 public static final int INTERRUPTION_FILTER_PRIORITY
116 = NotificationManager.INTERRUPTION_FILTER_PRIORITY;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400117
Christoph Studer85a384b2014-08-27 20:16:15 +0200118 /**
119 * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
120 * No interruptions filter.
121 */
John Spurlock80774932015-05-07 17:38:50 -0400122 public static final int INTERRUPTION_FILTER_NONE
123 = NotificationManager.INTERRUPTION_FILTER_NONE;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400124
John Spurlock4f1163c2015-04-02 17:41:21 -0400125 /**
126 * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
127 * Alarms only interruption filter.
128 */
John Spurlock80774932015-05-07 17:38:50 -0400129 public static final int INTERRUPTION_FILTER_ALARMS
130 = NotificationManager.INTERRUPTION_FILTER_ALARMS;
John Spurlock4f1163c2015-04-02 17:41:21 -0400131
John Spurlock83104102015-02-12 23:25:12 -0500132 /** {@link #getCurrentInterruptionFilter() Interruption filter} constant - returned when
133 * the value is unavailable for any reason. For example, before the notification listener
134 * is connected.
135 *
136 * {@see #onListenerConnected()}
137 */
John Spurlock80774932015-05-07 17:38:50 -0400138 public static final int INTERRUPTION_FILTER_UNKNOWN
139 = NotificationManager.INTERRUPTION_FILTER_UNKNOWN;
John Spurlock83104102015-02-12 23:25:12 -0500140
John Spurlockd8afe3c2014-08-01 14:04:07 -0400141 /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
142 * should disable notification sound, vibrating and other visual or aural effects.
Christoph Studer85a384b2014-08-27 20:16:15 +0200143 * This does not change the interruption filter, only the effects. **/
144 public static final int HINT_HOST_DISABLE_EFFECTS = 1;
John Spurlock1fa865f2014-07-21 14:56:39 -0400145
Bryce Lee7219ada2016-04-08 10:54:23 -0700146 /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
147 * should disable notification sound, but not phone calls.
148 * This does not change the interruption filter, only the effects. **/
149 public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 1 << 1;
150
151 /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
152 * should disable phone call sounds, buyt not notification sound.
153 * This does not change the interruption filter, only the effects. **/
154 public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 1 << 2;
155
Julia Reynoldsd5607292016-02-05 15:25:58 -0500156 /**
157 * Whether notification suppressed by DND should not interruption visually when the screen is
158 * off.
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500159 *
160 * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}.
Julia Reynoldsd5607292016-02-05 15:25:58 -0500161 */
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500162 @Deprecated
Julia Reynoldsd5607292016-02-05 15:25:58 -0500163 public static final int SUPPRESSED_EFFECT_SCREEN_OFF =
164 NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
165 /**
166 * Whether notification suppressed by DND should not interruption visually when the screen is
167 * on.
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500168 *
169 * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}.
Julia Reynoldsd5607292016-02-05 15:25:58 -0500170 */
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500171 @Deprecated
Julia Reynolds61721582016-01-05 08:35:25 -0500172 public static final int SUPPRESSED_EFFECT_SCREEN_ON =
173 NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500174
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -0500175
176 // Notification cancellation reasons
177
Julia Reynoldsa11d0b12017-02-16 15:01:36 -0500178 /** Notification was canceled by the status bar reporting a notification click. */
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400179 public static final int REASON_CLICK = 1;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -0500180 /** Notification was canceled by the status bar reporting a user dismissal. */
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400181 public static final int REASON_CANCEL = 2;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -0500182 /** Notification was canceled by the status bar reporting a user dismiss all. */
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400183 public static final int REASON_CANCEL_ALL = 3;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -0500184 /** Notification was canceled by the status bar reporting an inflation error. */
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400185 public static final int REASON_ERROR = 4;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -0500186 /** Notification was canceled by the package manager modifying the package. */
187 public static final int REASON_PACKAGE_CHANGED = 5;
188 /** Notification was canceled by the owning user context being stopped. */
189 public static final int REASON_USER_STOPPED = 6;
190 /** Notification was canceled by the user banning the package. */
191 public static final int REASON_PACKAGE_BANNED = 7;
192 /** Notification was canceled by the app canceling this specific notification. */
193 public static final int REASON_APP_CANCEL = 8;
194 /** Notification was canceled by the app cancelling all its notifications. */
195 public static final int REASON_APP_CANCEL_ALL = 9;
196 /** Notification was canceled by a listener reporting a user dismissal. */
197 public static final int REASON_LISTENER_CANCEL = 10;
198 /** Notification was canceled by a listener reporting a user dismiss all. */
199 public static final int REASON_LISTENER_CANCEL_ALL = 11;
200 /** Notification was canceled because it was a member of a canceled group. */
201 public static final int REASON_GROUP_SUMMARY_CANCELED = 12;
202 /** Notification was canceled because it was an invisible member of a group. */
203 public static final int REASON_GROUP_OPTIMIZATION = 13;
204 /** Notification was canceled by the device administrator suspending the package. */
205 public static final int REASON_PACKAGE_SUSPENDED = 14;
206 /** Notification was canceled by the owning managed profile being turned off. */
207 public static final int REASON_PROFILE_TURNED_OFF = 15;
208 /** Autobundled summary notification was canceled because its group was unbundled */
209 public static final int REASON_UNAUTOBUNDLED = 16;
210 /** Notification was canceled by the user banning the channel. */
211 public static final int REASON_CHANNEL_BANNED = 17;
212 /** Notification was snoozed. */
213 public static final int REASON_SNOOZED = 18;
Julia Reynolds2a128742016-11-28 14:29:25 -0500214 /** Notification was canceled due to timeout */
215 public static final int REASON_TIMEOUT = 19;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -0500216
Christoph Studerb82bc782014-08-20 14:29:43 +0200217 /**
218 * The full trim of the StatusBarNotification including all its features.
219 *
220 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -0400221 * @removed
Christoph Studerb82bc782014-08-20 14:29:43 +0200222 */
223 @SystemApi
224 public static final int TRIM_FULL = 0;
225
226 /**
227 * A light trim of the StatusBarNotification excluding the following features:
228 *
229 * <ol>
230 * <li>{@link Notification#tickerView tickerView}</li>
231 * <li>{@link Notification#contentView contentView}</li>
232 * <li>{@link Notification#largeIcon largeIcon}</li>
233 * <li>{@link Notification#bigContentView bigContentView}</li>
234 * <li>{@link Notification#headsUpContentView headsUpContentView}</li>
235 * <li>{@link Notification#EXTRA_LARGE_ICON extras[EXTRA_LARGE_ICON]}</li>
236 * <li>{@link Notification#EXTRA_LARGE_ICON_BIG extras[EXTRA_LARGE_ICON_BIG]}</li>
237 * <li>{@link Notification#EXTRA_PICTURE extras[EXTRA_PICTURE]}</li>
Christoph Studer223f44e2014-09-02 14:59:32 +0200238 * <li>{@link Notification#EXTRA_BIG_TEXT extras[EXTRA_BIG_TEXT]}</li>
Christoph Studerb82bc782014-08-20 14:29:43 +0200239 * </ol>
240 *
241 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -0400242 * @removed
Christoph Studerb82bc782014-08-20 14:29:43 +0200243 */
244 @SystemApi
245 public static final int TRIM_LIGHT = 1;
246
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400247
248 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700249 @IntDef(prefix = { "NOTIFICATION_CHANNEL_OR_GROUP_" }, value = {
250 NOTIFICATION_CHANNEL_OR_GROUP_ADDED,
251 NOTIFICATION_CHANNEL_OR_GROUP_UPDATED,
252 NOTIFICATION_CHANNEL_OR_GROUP_DELETED
253 })
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400254 @Retention(RetentionPolicy.SOURCE)
255 public @interface ChannelOrGroupModificationTypes {}
256
257 /**
258 * Channel or group modification reason provided to
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400259 * {@link #onNotificationChannelModified(String, UserHandle,NotificationChannel, int)} or
260 * {@link #onNotificationChannelGroupModified(String, UserHandle, NotificationChannelGroup,
261 * int)}- the provided object was created.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400262 */
263 public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1;
264
265 /**
266 * Channel or group modification reason provided to
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400267 * {@link #onNotificationChannelModified(String, UserHandle, NotificationChannel, int)} or
268 * {@link #onNotificationChannelGroupModified(String, UserHandle,NotificationChannelGroup, int)}
269 * - the provided object was updated.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400270 */
271 public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2;
272
273 /**
274 * Channel or group modification reason provided to
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400275 * {@link #onNotificationChannelModified(String, UserHandle, NotificationChannel, int)} or
276 * {@link #onNotificationChannelGroupModified(String, UserHandle, NotificationChannelGroup,
277 * int)}- the provided object was deleted.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400278 */
279 public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3;
280
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800281 private final Object mLock = new Object();
282
Mathew Inwood8c854f82018-09-14 12:35:36 +0100283 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800284 private Handler mHandler;
285
Chris Wren51017d02015-12-15 15:34:46 -0500286 /** @hide */
Mathew Inwoode3807372018-08-10 09:51:03 +0100287 @UnsupportedAppUsage
Chris Wren51017d02015-12-15 15:34:46 -0500288 protected NotificationListenerWrapper mWrapper = null;
Chris Wren5717bd62016-04-06 18:15:46 -0400289 private boolean isConnected = false;
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800290
291 @GuardedBy("mLock")
Christoph Studerd0694b62014-06-04 16:36:01 +0200292 private RankingMap mRankingMap;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700293
Julia Reynolds503ed942017-10-04 16:04:56 -0400294 /**
295 * @hide
296 */
Mathew Inwood31755f92018-12-20 13:53:36 +0000297 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Julia Reynolds503ed942017-10-04 16:04:56 -0400298 protected INotificationManager mNoMan;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700299
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500300 /**
301 * Only valid after a successful call to (@link registerAsService}.
302 * @hide
303 */
304 protected int mCurrentUser;
Chris Wren1941fc72014-05-14 15:20:51 -0400305
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500306 /**
307 * This context is required for system services since NotificationListenerService isn't
308 * started as a real Service and hence no context is available..
309 * @hide
310 */
311 protected Context mSystemContext;
Christoph Studer4600f9b2014-07-22 22:44:43 +0200312
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700313 /**
314 * The {@link Intent} that must be declared as handled by the service.
315 */
316 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
317 public static final String SERVICE_INTERFACE
318 = "android.service.notification.NotificationListenerService";
319
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800320 @Override
321 protected void attachBaseContext(Context base) {
322 super.attachBaseContext(base);
323 mHandler = new MyHandler(getMainLooper());
324 }
325
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800326 /**
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700327 * Implement this method to learn about new notifications as they are posted by apps.
328 *
329 * @param sbn A data structure encapsulating the original {@link android.app.Notification}
330 * object as well as its identifying information (tag and id) and source
331 * (package name).
332 */
Christoph Studerd0694b62014-06-04 16:36:01 +0200333 public void onNotificationPosted(StatusBarNotification sbn) {
334 // optional
335 }
336
337 /**
338 * Implement this method to learn about new notifications as they are posted by apps.
339 *
340 * @param sbn A data structure encapsulating the original {@link android.app.Notification}
341 * object as well as its identifying information (tag and id) and source
342 * (package name).
343 * @param rankingMap The current ranking map that can be used to retrieve ranking information
344 * for active notifications, including the newly posted one.
345 */
346 public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
347 onNotificationPosted(sbn);
348 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700349
350 /**
351 * Implement this method to learn when notifications are removed.
Chris Wren5717bd62016-04-06 18:15:46 -0400352 * <p>
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700353 * This might occur because the user has dismissed the notification using system UI (or another
354 * notification listener) or because the app has withdrawn the notification.
Chris Wren5717bd62016-04-06 18:15:46 -0400355 * <p>
Daniel Sandler1a497d32013-04-18 14:52:45 -0400356 * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
Scott Main04667da2013-04-25 16:57:16 -0700357 * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
Daniel Sandler1a497d32013-04-18 14:52:45 -0400358 * fields such as {@link android.app.Notification#contentView} and
359 * {@link android.app.Notification#largeIcon}. However, all other fields on
360 * {@link StatusBarNotification}, sufficient to match this call with a prior call to
361 * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700362 *
Daniel Sandler1a497d32013-04-18 14:52:45 -0400363 * @param sbn A data structure encapsulating at least the original information (tag and id)
364 * and source (package name) used to post the {@link android.app.Notification} that
365 * was just removed.
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700366 */
Christoph Studerd0694b62014-06-04 16:36:01 +0200367 public void onNotificationRemoved(StatusBarNotification sbn) {
368 // optional
369 }
370
371 /**
372 * Implement this method to learn when notifications are removed.
Chris Wren5717bd62016-04-06 18:15:46 -0400373 * <p>
Christoph Studerd0694b62014-06-04 16:36:01 +0200374 * This might occur because the user has dismissed the notification using system UI (or another
375 * notification listener) or because the app has withdrawn the notification.
Chris Wren5717bd62016-04-06 18:15:46 -0400376 * <p>
Christoph Studerd0694b62014-06-04 16:36:01 +0200377 * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
378 * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
379 * fields such as {@link android.app.Notification#contentView} and
380 * {@link android.app.Notification#largeIcon}. However, all other fields on
381 * {@link StatusBarNotification}, sufficient to match this call with a prior call to
382 * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
383 *
384 * @param sbn A data structure encapsulating at least the original information (tag and id)
385 * and source (package name) used to post the {@link android.app.Notification} that
386 * was just removed.
387 * @param rankingMap The current ranking map that can be used to retrieve ranking information
388 * for active notifications.
389 *
390 */
391 public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
392 onNotificationRemoved(sbn);
393 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700394
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -0500395
396 /**
397 * Implement this method to learn when notifications are removed and why.
398 * <p>
399 * This might occur because the user has dismissed the notification using system UI (or another
400 * notification listener) or because the app has withdrawn the notification.
401 * <p>
402 * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
403 * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
404 * fields such as {@link android.app.Notification#contentView} and
405 * {@link android.app.Notification#largeIcon}. However, all other fields on
406 * {@link StatusBarNotification}, sufficient to match this call with a prior call to
407 * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
408 *
409 ** @param sbn A data structure encapsulating at least the original information (tag and id)
410 * and source (package name) used to post the {@link android.app.Notification} that
411 * was just removed.
412 * @param rankingMap The current ranking map that can be used to retrieve ranking information
413 * for active notifications.
414 * @param reason see {@link #REASON_LISTENER_CANCEL}, etc.
415 */
416 public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
417 int reason) {
418 onNotificationRemoved(sbn, rankingMap);
419 }
420
John Spurlocka4294292014-03-24 18:02:32 -0400421 /**
Julia Reynolds503ed942017-10-04 16:04:56 -0400422 * NotificationStats are not populated for notification listeners, so fall back to
423 * {@link #onNotificationRemoved(StatusBarNotification, RankingMap, int)}.
424 *
425 * @hide
426 */
427 @TestApi
Julia Reynoldsd0ceefa2019-03-03 16:10:52 -0500428 @SystemApi
429 public void onNotificationRemoved(@NonNull StatusBarNotification sbn,
430 @NonNull RankingMap rankingMap, @NonNull NotificationStats stats, int reason) {
Julia Reynolds503ed942017-10-04 16:04:56 -0400431 onNotificationRemoved(sbn, rankingMap, reason);
432 }
433
434 /**
John Spurlocka4294292014-03-24 18:02:32 -0400435 * Implement this method to learn about when the listener is enabled and connected to
Christoph Studercee44ba2014-05-20 18:36:43 +0200436 * the notification manager. You are safe to call {@link #getActiveNotifications()}
John Spurlocka4294292014-03-24 18:02:32 -0400437 * at this time.
John Spurlocka4294292014-03-24 18:02:32 -0400438 */
Christoph Studercee44ba2014-05-20 18:36:43 +0200439 public void onListenerConnected() {
John Spurlocka4294292014-03-24 18:02:32 -0400440 // optional
441 }
442
Chris Wrenf9536642014-04-17 10:01:54 -0400443 /**
Chris Wren5717bd62016-04-06 18:15:46 -0400444 * Implement this method to learn about when the listener is disconnected from the
445 * notification manager.You will not receive any events after this call, and may only
446 * call {@link #requestRebind(ComponentName)} at this time.
447 */
448 public void onListenerDisconnected() {
449 // optional
450 }
451
452 /**
Christoph Studer05ad4822014-05-16 14:16:03 +0200453 * Implement this method to be notified when the notification ranking changes.
Christoph Studerd0694b62014-06-04 16:36:01 +0200454 *
455 * @param rankingMap The current ranking map that can be used to retrieve ranking information
456 * for active notifications.
Chris Wrenf9536642014-04-17 10:01:54 -0400457 */
Christoph Studerd0694b62014-06-04 16:36:01 +0200458 public void onNotificationRankingUpdate(RankingMap rankingMap) {
Chris Wrenf9536642014-04-17 10:01:54 -0400459 // optional
460 }
461
John Spurlock1fa865f2014-07-21 14:56:39 -0400462 /**
463 * Implement this method to be notified when the
John Spurlockd8afe3c2014-08-01 14:04:07 -0400464 * {@link #getCurrentListenerHints() Listener hints} change.
John Spurlock1fa865f2014-07-21 14:56:39 -0400465 *
John Spurlockd8afe3c2014-08-01 14:04:07 -0400466 * @param hints The current {@link #getCurrentListenerHints() listener hints}.
John Spurlock1fa865f2014-07-21 14:56:39 -0400467 */
John Spurlockd8afe3c2014-08-01 14:04:07 -0400468 public void onListenerHintsChanged(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -0400469 // optional
470 }
471
Christoph Studer85a384b2014-08-27 20:16:15 +0200472 /**
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500473 * Implement this method to be notified when the behavior of silent notifications in the status
474 * bar changes. See {@link NotificationManager#shouldHideSilentStatusBarIcons()}.
475 *
476 * @param hideSilentStatusIcons whether or not status bar icons should be hidden for silent
477 * notifications
478 */
479 public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
480 // optional
481 }
482
483 /**
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400484 * Implement this method to learn about notification channel modifications.
485 *
486 * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
487 * device} in order to receive this callback.
488 *
489 * @param pkg The package the channel belongs to.
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400490 * @param user The user on which the change was made.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400491 * @param channel The channel that has changed.
492 * @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED},
493 * {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
494 * {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
495 */
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400496 public void onNotificationChannelModified(String pkg, UserHandle user,
497 NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400498 // optional
499 }
500
501 /**
502 * Implement this method to learn about notification channel group modifications.
503 *
504 * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
505 * device} in order to receive this callback.
506 *
507 * @param pkg The package the group belongs to.
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400508 * @param user The user on which the change was made.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400509 * @param group The group that has changed.
510 * @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED},
511 * {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
512 * {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
513 */
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400514 public void onNotificationChannelGroupModified(String pkg, UserHandle user,
515 NotificationChannelGroup group, @ChannelOrGroupModificationTypes int modificationType) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400516 // optional
517 }
518
519 /**
Christoph Studer85a384b2014-08-27 20:16:15 +0200520 * Implement this method to be notified when the
521 * {@link #getCurrentInterruptionFilter() interruption filter} changed.
522 *
523 * @param interruptionFilter The current
524 * {@link #getCurrentInterruptionFilter() interruption filter}.
525 */
526 public void onInterruptionFilterChanged(int interruptionFilter) {
527 // optional
528 }
529
Chris Wren51017d02015-12-15 15:34:46 -0500530 /** @hide */
Mathew Inwood31755f92018-12-20 13:53:36 +0000531 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Chris Wren51017d02015-12-15 15:34:46 -0500532 protected final INotificationManager getNotificationInterface() {
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700533 if (mNoMan == null) {
534 mNoMan = INotificationManager.Stub.asInterface(
535 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
536 }
537 return mNoMan;
538 }
539
540 /**
541 * Inform the notification manager about dismissal of a single notification.
542 * <p>
543 * Use this if your listener has a user interface that allows the user to dismiss individual
544 * notifications, similar to the behavior of Android's status bar and notification panel.
545 * It should be called after the user dismisses a single notification using your UI;
546 * upon being informed, the notification manager will actually remove the notification
547 * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
Chris Wren5717bd62016-04-06 18:15:46 -0400548 * <p>
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700549 * <b>Note:</b> If your listener allows the user to fire a notification's
550 * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
551 * this method at that time <i>if</i> the Notification in question has the
552 * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
553 *
Chris Wren5717bd62016-04-06 18:15:46 -0400554 * <p>The service should wait for the {@link #onListenerConnected()} event
555 * before performing this operation.
556 *
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700557 * @param pkg Package of the notifying app.
558 * @param tag Tag of the notification as specified by the notifying app in
559 * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
560 * @param id ID of the notification as specified by the notifying app in
561 * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
Kenny Guya263e4e2014-03-03 18:24:03 +0000562 * <p>
563 * @deprecated Use {@link #cancelNotification(String key)}
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700564 * instead. Beginning with {@link android.os.Build.VERSION_CODES#LOLLIPOP} this method will no longer
Kenny Guya263e4e2014-03-03 18:24:03 +0000565 * cancel the notification. It will continue to cancel the notification for applications
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700566 * whose {@code targetSdkVersion} is earlier than {@link android.os.Build.VERSION_CODES#LOLLIPOP}.
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700567 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700568 @Deprecated
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400569 public final void cancelNotification(String pkg, String tag, int id) {
John Spurlockda9a3be2014-02-12 12:12:26 -0500570 if (!isBound()) return;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700571 try {
Kenny Guya263e4e2014-03-03 18:24:03 +0000572 getNotificationInterface().cancelNotificationFromListener(
573 mWrapper, pkg, tag, id);
574 } catch (android.os.RemoteException ex) {
575 Log.v(TAG, "Unable to contact notification manager", ex);
576 }
577 }
578
579 /**
580 * Inform the notification manager about dismissal of a single notification.
581 * <p>
582 * Use this if your listener has a user interface that allows the user to dismiss individual
583 * notifications, similar to the behavior of Android's status bar and notification panel.
584 * It should be called after the user dismisses a single notification using your UI;
585 * upon being informed, the notification manager will actually remove the notification
586 * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
Chris Wren5717bd62016-04-06 18:15:46 -0400587 * <p>
Kenny Guya263e4e2014-03-03 18:24:03 +0000588 * <b>Note:</b> If your listener allows the user to fire a notification's
589 * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
590 * this method at that time <i>if</i> the Notification in question has the
591 * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
592 * <p>
Chris Wren5717bd62016-04-06 18:15:46 -0400593 *
594 * <p>The service should wait for the {@link #onListenerConnected()} event
595 * before performing this operation.
596 *
Kenny Guya263e4e2014-03-03 18:24:03 +0000597 * @param key Notification to dismiss from {@link StatusBarNotification#getKey()}.
598 */
599 public final void cancelNotification(String key) {
600 if (!isBound()) return;
601 try {
602 getNotificationInterface().cancelNotificationsFromListener(mWrapper,
Daniel Sandlerf5a78382015-05-15 23:59:36 -0400603 new String[] { key });
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700604 } catch (android.os.RemoteException ex) {
605 Log.v(TAG, "Unable to contact notification manager", ex);
606 }
607 }
608
609 /**
610 * Inform the notification manager about dismissal of all notifications.
611 * <p>
612 * Use this if your listener has a user interface that allows the user to dismiss all
613 * notifications, similar to the behavior of Android's status bar and notification panel.
614 * It should be called after the user invokes the "dismiss all" function of your UI;
615 * upon being informed, the notification manager will actually remove all active notifications
616 * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks.
617 *
Chris Wren5717bd62016-04-06 18:15:46 -0400618 * <p>The service should wait for the {@link #onListenerConnected()} event
619 * before performing this operation.
620 *
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400621 * {@see #cancelNotification(String, String, int)}
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700622 */
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400623 public final void cancelAllNotifications() {
John Spurlocka4294292014-03-24 18:02:32 -0400624 cancelNotifications(null /*all*/);
625 }
626
627 /**
628 * Inform the notification manager about dismissal of specific notifications.
629 * <p>
630 * Use this if your listener has a user interface that allows the user to dismiss
631 * multiple notifications at once.
632 *
Chris Wren5717bd62016-04-06 18:15:46 -0400633 * <p>The service should wait for the {@link #onListenerConnected()} event
634 * before performing this operation.
635 *
John Spurlocka4294292014-03-24 18:02:32 -0400636 * @param keys Notifications to dismiss, or {@code null} to dismiss all.
637 *
638 * {@see #cancelNotification(String, String, int)}
639 */
640 public final void cancelNotifications(String[] keys) {
John Spurlockda9a3be2014-02-12 12:12:26 -0500641 if (!isBound()) return;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700642 try {
John Spurlocka4294292014-03-24 18:02:32 -0400643 getNotificationInterface().cancelNotificationsFromListener(mWrapper, keys);
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700644 } catch (android.os.RemoteException ex) {
645 Log.v(TAG, "Unable to contact notification manager", ex);
646 }
647 }
648
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400649 /**
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400650 * Inform the notification manager about snoozing a specific notification.
651 * <p>
652 * Use this if your listener has a user interface that allows the user to snooze a notification
Julia Reynolds79672302017-01-12 08:30:16 -0500653 * until a given {@link SnoozeCriterion}. It should be called after the user snoozes a single
654 * notification using your UI; upon being informed, the notification manager will actually
655 * remove the notification and you will get an
656 * {@link #onNotificationRemoved(StatusBarNotification)} callback. When the snoozing period
657 * expires, you will get a {@link #onNotificationPosted(StatusBarNotification, RankingMap)}
658 * callback for the notification.
659 * @param key The key of the notification to snooze
660 * @param snoozeCriterionId The{@link SnoozeCriterion#getId()} of a context to snooze the
661 * notification until.
Julia Reynolds1327d3c2017-02-17 09:26:45 -0500662 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -0400663 * @removed
Julia Reynolds79672302017-01-12 08:30:16 -0500664 */
Julia Reynolds1327d3c2017-02-17 09:26:45 -0500665 @SystemApi
Julia Reynolds79672302017-01-12 08:30:16 -0500666 public final void snoozeNotification(String key, String snoozeCriterionId) {
667 if (!isBound()) return;
668 try {
669 getNotificationInterface().snoozeNotificationUntilContextFromListener(
670 mWrapper, key, snoozeCriterionId);
671 } catch (android.os.RemoteException ex) {
672 Log.v(TAG, "Unable to contact notification manager", ex);
673 }
674 }
675
676 /**
677 * Inform the notification manager about snoozing a specific notification.
678 * <p>
679 * Use this if your listener has a user interface that allows the user to snooze a notification
Julia Reynolds50989772017-02-23 14:32:16 -0500680 * for a time. It should be called after the user snoozes a single notification using
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400681 * your UI; upon being informed, the notification manager will actually remove the notification
682 * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. When the
683 * snoozing period expires, you will get a
684 * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
685 * notification.
686 * @param key The key of the notification to snooze
Julia Reynolds50989772017-02-23 14:32:16 -0500687 * @param durationMs A duration to snooze the notification for, in milliseconds.
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400688 */
Julia Reynolds50989772017-02-23 14:32:16 -0500689 public final void snoozeNotification(String key, long durationMs) {
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400690 if (!isBound()) return;
691 try {
Julia Reynoldsb6c1f992016-11-22 09:26:46 -0500692 getNotificationInterface().snoozeNotificationUntilFromListener(
Julia Reynolds50989772017-02-23 14:32:16 -0500693 mWrapper, key, durationMs);
Julia Reynoldsb6c1f992016-11-22 09:26:46 -0500694 } catch (android.os.RemoteException ex) {
695 Log.v(TAG, "Unable to contact notification manager", ex);
696 }
697 }
698
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400699
700 /**
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700701 * Inform the notification manager that these notifications have been viewed by the
Amith Yamasanic6ecbce2015-06-23 12:58:43 -0700702 * user. This should only be called when there is sufficient confidence that the user is
703 * looking at the notifications, such as when the notifications appear on the screen due to
704 * an explicit user interaction.
Chris Wren5717bd62016-04-06 18:15:46 -0400705 *
706 * <p>The service should wait for the {@link #onListenerConnected()} event
707 * before performing this operation.
708 *
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700709 * @param keys Notifications to mark as seen.
710 */
711 public final void setNotificationsShown(String[] keys) {
712 if (!isBound()) return;
713 try {
714 getNotificationInterface().setNotificationsShownFromListener(mWrapper, keys);
715 } catch (android.os.RemoteException ex) {
716 Log.v(TAG, "Unable to contact notification manager", ex);
717 }
718 }
719
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400720
721 /**
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400722 * Updates a notification channel for a given package for a given user. This should only be used
723 * to reflect changes a user has made to the channel via the listener's user interface.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400724 *
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400725 * <p>This method will throw a security exception if you don't have access to notifications
726 * for the given user.</p>
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400727 * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
728 * device} in order to use this method.
729 *
730 * @param pkg The package the channel belongs to.
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400731 * @param user The user the channel belongs to.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400732 * @param channel the channel to update.
733 */
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400734 public final void updateNotificationChannel(@NonNull String pkg, @NonNull UserHandle user,
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400735 @NonNull NotificationChannel channel) {
736 if (!isBound()) return;
737 try {
738 getNotificationInterface().updateNotificationChannelFromPrivilegedListener(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400739 mWrapper, pkg, user, channel);
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400740 } catch (RemoteException e) {
741 Log.v(TAG, "Unable to contact notification manager", e);
742 throw e.rethrowFromSystemServer();
743 }
744 }
745
746 /**
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400747 * Returns all notification channels belonging to the given package for a given user.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400748 *
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400749 * <p>This method will throw a security exception if you don't have access to notifications
750 * for the given user.</p>
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400751 * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
Julia Reynolds48a6ed92018-10-22 12:52:03 -0400752 * device} or be the {@link NotificationAssistantService notification assistant} in order to
753 * use this method.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400754 *
755 * @param pkg The package to retrieve channels for.
756 */
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400757 public final List<NotificationChannel> getNotificationChannels(@NonNull String pkg,
758 @NonNull UserHandle user) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400759 if (!isBound()) return null;
760 try {
761
762 return getNotificationInterface().getNotificationChannelsFromPrivilegedListener(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400763 mWrapper, pkg, user).getList();
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400764 } catch (RemoteException e) {
765 Log.v(TAG, "Unable to contact notification manager", e);
766 throw e.rethrowFromSystemServer();
767 }
768 }
769
770 /**
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400771 * Returns all notification channel groups belonging to the given package for a given user.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400772 *
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400773 * <p>This method will throw a security exception if you don't have access to notifications
774 * for the given user.</p>
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400775 * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
Julia Reynolds48a6ed92018-10-22 12:52:03 -0400776 * device} or be the {@link NotificationAssistantService notification assistant} in order to
777 * use this method.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400778 *
779 * @param pkg The package to retrieve channel groups for.
780 */
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400781 public final List<NotificationChannelGroup> getNotificationChannelGroups(@NonNull String pkg,
782 @NonNull UserHandle user) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400783 if (!isBound()) return null;
784 try {
785
786 return getNotificationInterface().getNotificationChannelGroupsFromPrivilegedListener(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400787 mWrapper, pkg, user).getList();
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400788 } catch (RemoteException e) {
789 Log.v(TAG, "Unable to contact notification manager", e);
790 throw e.rethrowFromSystemServer();
791 }
792 }
793
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700794 /**
Christoph Studerb82bc782014-08-20 14:29:43 +0200795 * Sets the notification trim that will be received via {@link #onNotificationPosted}.
796 *
797 * <p>
798 * Setting a trim other than {@link #TRIM_FULL} enables listeners that don't need access to the
799 * full notification features right away to reduce their memory footprint. Full notifications
800 * can be requested on-demand via {@link #getActiveNotifications(int)}.
801 *
802 * <p>
803 * Set to {@link #TRIM_FULL} initially.
804 *
Chris Wren5717bd62016-04-06 18:15:46 -0400805 * <p>The service should wait for the {@link #onListenerConnected()} event
806 * before performing this operation.
807 *
Christoph Studerb82bc782014-08-20 14:29:43 +0200808 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -0400809 * @removed
Christoph Studerb82bc782014-08-20 14:29:43 +0200810 *
811 * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}.
812 * See <code>TRIM_*</code> constants.
813 */
814 @SystemApi
815 public final void setOnNotificationPostedTrim(int trim) {
816 if (!isBound()) return;
817 try {
818 getNotificationInterface().setOnNotificationPostedTrimFromListener(mWrapper, trim);
819 } catch (RemoteException ex) {
820 Log.v(TAG, "Unable to contact notification manager", ex);
821 }
822 }
823
824 /**
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400825 * Request the list of outstanding notifications (that is, those that are visible to the
John Spurlocka4294292014-03-24 18:02:32 -0400826 * current user). Useful when you don't know what's already been posted.
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400827 *
Chris Wren5717bd62016-04-06 18:15:46 -0400828 * <p>The service should wait for the {@link #onListenerConnected()} event
829 * before performing this operation.
830 *
Chris Wrenf9536642014-04-17 10:01:54 -0400831 * @return An array of active notifications, sorted in natural order.
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400832 */
833 public StatusBarNotification[] getActiveNotifications() {
Julia Reynolds94a38b32018-04-20 13:33:36 -0400834 StatusBarNotification[] activeNotifications = getActiveNotifications(null, TRIM_FULL);
835 return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
Christoph Studerb82bc782014-08-20 14:29:43 +0200836 }
837
838 /**
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500839 * Like {@link #getActiveNotifications()}, but returns the list of currently snoozed
840 * notifications, for all users this listener has access to.
841 *
842 * <p>The service should wait for the {@link #onListenerConnected()} event
843 * before performing this operation.
844 *
Julia Reynoldsa11d0b12017-02-16 15:01:36 -0500845 * @return An array of snoozed notifications, sorted in natural order.
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500846 */
847 public final StatusBarNotification[] getSnoozedNotifications() {
848 try {
849 ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
850 .getSnoozedNotificationsFromListener(mWrapper, TRIM_FULL);
851 return cleanUpNotificationList(parceledList);
852 } catch (android.os.RemoteException ex) {
853 Log.v(TAG, "Unable to contact notification manager", ex);
854 }
855 return null;
856 }
857
858 /**
Christoph Studerb82bc782014-08-20 14:29:43 +0200859 * Request the list of outstanding notifications (that is, those that are visible to the
860 * current user). Useful when you don't know what's already been posted.
861 *
862 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -0400863 * @removed
Christoph Studerb82bc782014-08-20 14:29:43 +0200864 *
865 * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
866 * @return An array of active notifications, sorted in natural order.
867 */
868 @SystemApi
869 public StatusBarNotification[] getActiveNotifications(int trim) {
Julia Reynolds94a38b32018-04-20 13:33:36 -0400870 StatusBarNotification[] activeNotifications = getActiveNotifications(null, trim);
871 return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
Dan Sandlerea75fdd2014-08-12 12:29:19 -0400872 }
873
874 /**
875 * Request one or more notifications by key. Useful if you have been keeping track of
876 * notifications but didn't want to retain the bits, and now need to go back and extract
877 * more data out of those notifications.
878 *
Chris Wren5717bd62016-04-06 18:15:46 -0400879 * <p>The service should wait for the {@link #onListenerConnected()} event
880 * before performing this operation.
881 *
Christoph Studerb82bc782014-08-20 14:29:43 +0200882 * @param keys the keys of the notifications to request
Dan Sandlerea75fdd2014-08-12 12:29:19 -0400883 * @return An array of notifications corresponding to the requested keys, in the
884 * same order as the key list.
885 */
886 public StatusBarNotification[] getActiveNotifications(String[] keys) {
Julia Reynolds94a38b32018-04-20 13:33:36 -0400887 StatusBarNotification[] activeNotifications = getActiveNotifications(keys, TRIM_FULL);
888 return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
Christoph Studerb82bc782014-08-20 14:29:43 +0200889 }
890
891 /**
892 * Request one or more notifications by key. Useful if you have been keeping track of
893 * notifications but didn't want to retain the bits, and now need to go back and extract
894 * more data out of those notifications.
895 *
896 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -0400897 * @removed
Christoph Studerb82bc782014-08-20 14:29:43 +0200898 *
899 * @param keys the keys of the notifications to request
900 * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
901 * @return An array of notifications corresponding to the requested keys, in the
902 * same order as the key list.
903 */
904 @SystemApi
905 public StatusBarNotification[] getActiveNotifications(String[] keys, int trim) {
906 if (!isBound())
907 return null;
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400908 try {
Christoph Studerb82bc782014-08-20 14:29:43 +0200909 ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
910 .getActiveNotificationsFromListener(mWrapper, keys, trim);
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500911 return cleanUpNotificationList(parceledList);
John Spurlocka4294292014-03-24 18:02:32 -0400912 } catch (android.os.RemoteException ex) {
913 Log.v(TAG, "Unable to contact notification manager", ex);
914 }
915 return null;
916 }
917
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500918 private StatusBarNotification[] cleanUpNotificationList(
919 ParceledListSlice<StatusBarNotification> parceledList) {
Julia Reynolds94a38b32018-04-20 13:33:36 -0400920 if (parceledList == null || parceledList.getList() == null) {
921 return new StatusBarNotification[0];
922 }
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500923 List<StatusBarNotification> list = parceledList.getList();
924 ArrayList<StatusBarNotification> corruptNotifications = null;
925 int N = list.size();
926 for (int i = 0; i < N; i++) {
927 StatusBarNotification sbn = list.get(i);
928 Notification notification = sbn.getNotification();
929 try {
930 // convert icon metadata to legacy format for older clients
931 createLegacyIconExtras(notification);
932 // populate remote views for older clients.
933 maybePopulateRemoteViews(notification);
Selim Cineke7238dd2017-12-14 17:48:32 -0800934 // populate people for older clients.
935 maybePopulatePeople(notification);
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500936 } catch (IllegalArgumentException e) {
937 if (corruptNotifications == null) {
938 corruptNotifications = new ArrayList<>(N);
939 }
940 corruptNotifications.add(sbn);
941 Log.w(TAG, "get(Active/Snoozed)Notifications: can't rebuild notification from " +
942 sbn.getPackageName());
943 }
944 }
945 if (corruptNotifications != null) {
946 list.removeAll(corruptNotifications);
947 }
948 return list.toArray(new StatusBarNotification[list.size()]);
949 }
950
John Spurlocka4294292014-03-24 18:02:32 -0400951 /**
John Spurlockd8afe3c2014-08-01 14:04:07 -0400952 * Gets the set of hints representing current state.
John Spurlock1fa865f2014-07-21 14:56:39 -0400953 *
954 * <p>
John Spurlockd8afe3c2014-08-01 14:04:07 -0400955 * The current state may differ from the requested state if the hint represents state
John Spurlock1fa865f2014-07-21 14:56:39 -0400956 * shared across all listeners or a feature the notification host does not support or refuses
957 * to grant.
958 *
Chris Wren5717bd62016-04-06 18:15:46 -0400959 * <p>The service should wait for the {@link #onListenerConnected()} event
960 * before performing this operation.
961 *
Christoph Studer85a384b2014-08-27 20:16:15 +0200962 * @return Zero or more of the HINT_ constants.
John Spurlock1fa865f2014-07-21 14:56:39 -0400963 */
John Spurlockd8afe3c2014-08-01 14:04:07 -0400964 public final int getCurrentListenerHints() {
Christoph Studer85a384b2014-08-27 20:16:15 +0200965 if (!isBound()) return 0;
John Spurlock1fa865f2014-07-21 14:56:39 -0400966 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400967 return getNotificationInterface().getHintsFromListener(mWrapper);
John Spurlock1fa865f2014-07-21 14:56:39 -0400968 } catch (android.os.RemoteException ex) {
969 Log.v(TAG, "Unable to contact notification manager", ex);
Christoph Studer85a384b2014-08-27 20:16:15 +0200970 return 0;
971 }
972 }
973
974 /**
975 * Gets the current notification interruption filter active on the host.
976 *
977 * <p>
978 * The interruption filter defines which notifications are allowed to interrupt the user
979 * (e.g. via sound &amp; vibration) and is applied globally. Listeners can find out whether
980 * a specific notification matched the interruption filter via
981 * {@link Ranking#matchesInterruptionFilter()}.
982 * <p>
983 * The current filter may differ from the previously requested filter if the notification host
984 * does not support or refuses to apply the requested filter, or if another component changed
985 * the filter in the meantime.
986 * <p>
987 * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
988 *
Chris Wren5717bd62016-04-06 18:15:46 -0400989 * <p>The service should wait for the {@link #onListenerConnected()} event
990 * before performing this operation.
991 *
John Spurlock83104102015-02-12 23:25:12 -0500992 * @return One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when
993 * unavailable.
Christoph Studer85a384b2014-08-27 20:16:15 +0200994 */
995 public final int getCurrentInterruptionFilter() {
John Spurlock83104102015-02-12 23:25:12 -0500996 if (!isBound()) return INTERRUPTION_FILTER_UNKNOWN;
Christoph Studer85a384b2014-08-27 20:16:15 +0200997 try {
Chris Wren957ed702014-09-24 18:17:36 -0400998 return getNotificationInterface().getInterruptionFilterFromListener(mWrapper);
Christoph Studer85a384b2014-08-27 20:16:15 +0200999 } catch (android.os.RemoteException ex) {
1000 Log.v(TAG, "Unable to contact notification manager", ex);
John Spurlock83104102015-02-12 23:25:12 -05001001 return INTERRUPTION_FILTER_UNKNOWN;
John Spurlock1fa865f2014-07-21 14:56:39 -04001002 }
1003 }
1004
1005 /**
Julia Reynolds4703bac2018-09-12 10:39:30 -04001006 * Clears listener hints set via {@link #getCurrentListenerHints()}.
1007 *
1008 * <p>The service should wait for the {@link #onListenerConnected()} event
1009 * before performing this operation.
1010 */
1011 public final void clearRequestedListenerHints() {
1012 if (!isBound()) return;
1013 try {
1014 getNotificationInterface().clearRequestedListenerHints(mWrapper);
1015 } catch (android.os.RemoteException ex) {
1016 Log.v(TAG, "Unable to contact notification manager", ex);
1017 }
1018 }
1019
1020 /**
John Spurlockd8afe3c2014-08-01 14:04:07 -04001021 * Sets the desired {@link #getCurrentListenerHints() listener hints}.
John Spurlock1fa865f2014-07-21 14:56:39 -04001022 *
1023 * <p>
Christoph Studer85a384b2014-08-27 20:16:15 +02001024 * This is merely a request, the host may or may not choose to take action depending
John Spurlock1fa865f2014-07-21 14:56:39 -04001025 * on other listener requests or other global state.
1026 * <p>
John Spurlockd8afe3c2014-08-01 14:04:07 -04001027 * Listen for updates using {@link #onListenerHintsChanged(int)}.
John Spurlock1fa865f2014-07-21 14:56:39 -04001028 *
Chris Wren5717bd62016-04-06 18:15:46 -04001029 * <p>The service should wait for the {@link #onListenerConnected()} event
1030 * before performing this operation.
1031 *
John Spurlockd8afe3c2014-08-01 14:04:07 -04001032 * @param hints One or more of the HINT_ constants.
John Spurlock1fa865f2014-07-21 14:56:39 -04001033 */
John Spurlockd8afe3c2014-08-01 14:04:07 -04001034 public final void requestListenerHints(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04001035 if (!isBound()) return;
1036 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001037 getNotificationInterface().requestHintsFromListener(mWrapper, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04001038 } catch (android.os.RemoteException ex) {
1039 Log.v(TAG, "Unable to contact notification manager", ex);
1040 }
1041 }
1042
1043 /**
Christoph Studer85a384b2014-08-27 20:16:15 +02001044 * Sets the desired {@link #getCurrentInterruptionFilter() interruption filter}.
1045 *
1046 * <p>
1047 * This is merely a request, the host may or may not choose to apply the requested
1048 * interruption filter depending on other listener requests or other global state.
1049 * <p>
1050 * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
1051 *
Chris Wren5717bd62016-04-06 18:15:46 -04001052 * <p>The service should wait for the {@link #onListenerConnected()} event
1053 * before performing this operation.
1054 *
Christoph Studer85a384b2014-08-27 20:16:15 +02001055 * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants.
1056 */
1057 public final void requestInterruptionFilter(int interruptionFilter) {
1058 if (!isBound()) return;
1059 try {
1060 getNotificationInterface()
1061 .requestInterruptionFilterFromListener(mWrapper, interruptionFilter);
1062 } catch (android.os.RemoteException ex) {
1063 Log.v(TAG, "Unable to contact notification manager", ex);
1064 }
1065 }
1066
1067 /**
Christoph Studer05ad4822014-05-16 14:16:03 +02001068 * Returns current ranking information.
John Spurlocka4294292014-03-24 18:02:32 -04001069 *
Christoph Studer05ad4822014-05-16 14:16:03 +02001070 * <p>
1071 * The returned object represents the current ranking snapshot and only
Christoph Studerd0694b62014-06-04 16:36:01 +02001072 * applies for currently active notifications.
1073 * <p>
1074 * Generally you should use the RankingMap that is passed with events such
1075 * as {@link #onNotificationPosted(StatusBarNotification, RankingMap)},
1076 * {@link #onNotificationRemoved(StatusBarNotification, RankingMap)}, and
1077 * so on. This method should only be used when needing access outside of
1078 * such events, for example to retrieve the RankingMap right after
1079 * initialization.
Christoph Studer05ad4822014-05-16 14:16:03 +02001080 *
Chris Wren5717bd62016-04-06 18:15:46 -04001081 * <p>The service should wait for the {@link #onListenerConnected()} event
1082 * before performing this operation.
1083 *
Christoph Studerd0694b62014-06-04 16:36:01 +02001084 * @return A {@link RankingMap} object providing access to ranking information
John Spurlocka4294292014-03-24 18:02:32 -04001085 */
Christoph Studerd0694b62014-06-04 16:36:01 +02001086 public RankingMap getCurrentRanking() {
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001087 synchronized (mLock) {
1088 return mRankingMap;
1089 }
Daniel Sandler25cf8ce2013-04-24 15:34:57 -04001090 }
1091
Chris Wren5717bd62016-04-06 18:15:46 -04001092 /**
1093 * This is not the lifecycle event you are looking for.
1094 *
1095 * <p>The service should wait for the {@link #onListenerConnected()} event
1096 * before performing any operations.
1097 */
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001098 @Override
1099 public IBinder onBind(Intent intent) {
1100 if (mWrapper == null) {
Chris Wren51017d02015-12-15 15:34:46 -05001101 mWrapper = new NotificationListenerWrapper();
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001102 }
1103 return mWrapper;
1104 }
1105
Chris Wren51017d02015-12-15 15:34:46 -05001106 /** @hide */
Mathew Inwoode3807372018-08-10 09:51:03 +01001107 @UnsupportedAppUsage
Chris Wren51017d02015-12-15 15:34:46 -05001108 protected boolean isBound() {
John Spurlockda9a3be2014-02-12 12:12:26 -05001109 if (mWrapper == null) {
1110 Log.w(TAG, "Notification listener service not yet bound.");
1111 return false;
1112 }
1113 return true;
1114 }
1115
Chris Wren5717bd62016-04-06 18:15:46 -04001116 @Override
1117 public void onDestroy() {
1118 onListenerDisconnected();
1119 super.onDestroy();
1120 }
1121
Chris Wren1941fc72014-05-14 15:20:51 -04001122 /**
1123 * Directly register this service with the Notification Manager.
1124 *
1125 * <p>Only system services may use this call. It will fail for non-system callers.
1126 * Apps should ask the user to add their listener in Settings.
1127 *
Christoph Studer4600f9b2014-07-22 22:44:43 +02001128 * @param context Context required for accessing resources. Since this service isn't
1129 * launched as a real Service when using this method, a context has to be passed in.
Chris Wren1941fc72014-05-14 15:20:51 -04001130 * @param componentName the component that will consume the notification information
1131 * @param currentUser the user to use as the stream filter
1132 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -04001133 * @removed
Chris Wren1941fc72014-05-14 15:20:51 -04001134 */
Jeff Brown5c507c12014-06-05 17:14:39 -07001135 @SystemApi
Christoph Studer4600f9b2014-07-22 22:44:43 +02001136 public void registerAsSystemService(Context context, ComponentName componentName,
1137 int currentUser) throws RemoteException {
Chris Wren1941fc72014-05-14 15:20:51 -04001138 if (mWrapper == null) {
Chris Wren51017d02015-12-15 15:34:46 -05001139 mWrapper = new NotificationListenerWrapper();
Chris Wren1941fc72014-05-14 15:20:51 -04001140 }
Chris Wren0efdb882016-03-01 17:17:47 -05001141 mSystemContext = context;
Chris Wren1941fc72014-05-14 15:20:51 -04001142 INotificationManager noMan = getNotificationInterface();
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001143 mHandler = new MyHandler(context.getMainLooper());
Chris Wrenb66a16d2016-04-29 10:07:48 -04001144 mCurrentUser = currentUser;
1145 noMan.registerListener(mWrapper, componentName, currentUser);
Chris Wren1941fc72014-05-14 15:20:51 -04001146 }
1147
1148 /**
1149 * Directly unregister this service from the Notification Manager.
1150 *
Chris Wren5717bd62016-04-06 18:15:46 -04001151 * <p>This method will fail for listeners that were not registered
Chris Wren1941fc72014-05-14 15:20:51 -04001152 * with (@link registerAsService).
1153 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -04001154 * @removed
Chris Wren1941fc72014-05-14 15:20:51 -04001155 */
Jeff Brown5c507c12014-06-05 17:14:39 -07001156 @SystemApi
Chris Wren1941fc72014-05-14 15:20:51 -04001157 public void unregisterAsSystemService() throws RemoteException {
1158 if (mWrapper != null) {
1159 INotificationManager noMan = getNotificationInterface();
1160 noMan.unregisterListener(mWrapper, mCurrentUser);
1161 }
1162 }
1163
Chris Wrenab41eec2016-01-04 18:01:27 -05001164 /**
Elliot Waite54de7742017-01-11 15:30:35 -08001165 * Request that the listener be rebound, after a previous call to {@link #requestUnbind}.
Chris Wrenab41eec2016-01-04 18:01:27 -05001166 *
Chris Wren5717bd62016-04-06 18:15:46 -04001167 * <p>This method will fail for listeners that have
Chris Wrenab41eec2016-01-04 18:01:27 -05001168 * not been granted the permission by the user.
Chris Wrenab41eec2016-01-04 18:01:27 -05001169 */
Chris Wrencf548bf2016-05-20 14:53:16 -04001170 public static void requestRebind(ComponentName componentName) {
Chris Wrenab41eec2016-01-04 18:01:27 -05001171 INotificationManager noMan = INotificationManager.Stub.asInterface(
1172 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
Chris Wrencf548bf2016-05-20 14:53:16 -04001173 try {
1174 noMan.requestBindListener(componentName);
1175 } catch (RemoteException ex) {
1176 throw ex.rethrowFromSystemServer();
1177 }
Chris Wrenab41eec2016-01-04 18:01:27 -05001178 }
1179
1180 /**
1181 * Request that the service be unbound.
1182 *
Julia Reynolds0ffc13b2017-04-27 13:54:36 -04001183 * <p>Once this is called, you will no longer receive updates and no method calls are
1184 * guaranteed to be successful, until you next receive the {@link #onListenerConnected()} event.
1185 * The service will likely be killed by the system after this call.
Chris Wren5717bd62016-04-06 18:15:46 -04001186 *
1187 * <p>The service should wait for the {@link #onListenerConnected()} event
1188 * before performing this operation. I know it's tempting, but you must wait.
Chris Wrenab41eec2016-01-04 18:01:27 -05001189 */
Chris Wrencf548bf2016-05-20 14:53:16 -04001190 public final void requestUnbind() {
Chris Wrenab41eec2016-01-04 18:01:27 -05001191 if (mWrapper != null) {
1192 INotificationManager noMan = getNotificationInterface();
Chris Wrencf548bf2016-05-20 14:53:16 -04001193 try {
1194 noMan.requestUnbindListener(mWrapper);
1195 // Disable future messages.
1196 isConnected = false;
1197 } catch (RemoteException ex) {
1198 throw ex.rethrowFromSystemServer();
1199 }
Chris Wrenab41eec2016-01-04 18:01:27 -05001200 }
1201 }
1202
Daniel Sandlerf5a78382015-05-15 23:59:36 -04001203 /** Convert new-style Icons to legacy representations for pre-M clients. */
1204 private void createLegacyIconExtras(Notification n) {
1205 Icon smallIcon = n.getSmallIcon();
1206 Icon largeIcon = n.getLargeIcon();
Dan Sandler99a37f12015-06-09 14:34:38 -04001207 if (smallIcon != null && smallIcon.getType() == Icon.TYPE_RESOURCE) {
Daniel Sandlerf5a78382015-05-15 23:59:36 -04001208 n.extras.putInt(Notification.EXTRA_SMALL_ICON, smallIcon.getResId());
1209 n.icon = smallIcon.getResId();
1210 }
1211 if (largeIcon != null) {
1212 Drawable d = largeIcon.loadDrawable(getContext());
1213 if (d != null && d instanceof BitmapDrawable) {
1214 final Bitmap largeIconBits = ((BitmapDrawable) d).getBitmap();
1215 n.extras.putParcelable(Notification.EXTRA_LARGE_ICON, largeIconBits);
1216 n.largeIcon = largeIconBits;
1217 }
1218 }
1219 }
1220
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001221 /**
1222 * Populates remote views for pre-N targeting apps.
1223 */
1224 private void maybePopulateRemoteViews(Notification notification) {
1225 if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
1226 Builder builder = Builder.recoverBuilder(getContext(), notification);
Adrian Roos5081c0d2016-02-26 16:04:19 -08001227
1228 // Some styles wrap Notification's contentView, bigContentView and headsUpContentView.
1229 // First inflate them all, only then set them to avoid recursive wrapping.
1230 RemoteViews content = builder.createContentView();
1231 RemoteViews big = builder.createBigContentView();
1232 RemoteViews headsUp = builder.createHeadsUpContentView();
1233
1234 notification.contentView = content;
1235 notification.bigContentView = big;
1236 notification.headsUpContentView = headsUp;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001237 }
1238 }
1239
Selim Cineke7238dd2017-12-14 17:48:32 -08001240 /**
1241 * Populates remote views for pre-P targeting apps.
1242 */
1243 private void maybePopulatePeople(Notification notification) {
1244 if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P) {
Selim Cinek9acd6732018-03-23 16:39:02 -07001245 ArrayList<Person> people = notification.extras.getParcelableArrayList(
Selim Cineke7238dd2017-12-14 17:48:32 -08001246 Notification.EXTRA_PEOPLE_LIST);
1247 if (people != null && people.isEmpty()) {
1248 int size = people.size();
1249 String[] peopleArray = new String[size];
1250 for (int i = 0; i < size; i++) {
Selim Cinek9acd6732018-03-23 16:39:02 -07001251 Person person = people.get(i);
Selim Cineke7238dd2017-12-14 17:48:32 -08001252 peopleArray[i] = person.resolveToLegacyUri();
1253 }
1254 notification.extras.putStringArray(Notification.EXTRA_PEOPLE, peopleArray);
1255 }
1256 }
1257 }
1258
Chris Wren51017d02015-12-15 15:34:46 -05001259 /** @hide */
1260 protected class NotificationListenerWrapper extends INotificationListener.Stub {
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001261 @Override
Griff Hazen84a00ea2014-09-02 17:10:47 -07001262 public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
Christoph Studer05ad4822014-05-16 14:16:03 +02001263 NotificationRankingUpdate update) {
Griff Hazen84a00ea2014-09-02 17:10:47 -07001264 StatusBarNotification sbn;
1265 try {
1266 sbn = sbnHolder.get();
1267 } catch (RemoteException e) {
1268 Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);
1269 return;
1270 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02001271
Chris Wren24fb8942015-06-18 14:33:56 -04001272 try {
Chris Wren24fb8942015-06-18 14:33:56 -04001273 // convert icon metadata to legacy format for older clients
1274 createLegacyIconExtras(sbn.getNotification());
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001275 maybePopulateRemoteViews(sbn.getNotification());
Selim Cinek5390e7d2018-02-20 19:12:41 -08001276 maybePopulatePeople(sbn.getNotification());
Chris Wren24fb8942015-06-18 14:33:56 -04001277 } catch (IllegalArgumentException e) {
Andreas Gampe1ed71f32015-12-11 15:49:07 -08001278 // warn and drop corrupt notification
Chris Wren24fb8942015-06-18 14:33:56 -04001279 Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +
1280 sbn.getPackageName());
Andreas Gampe1ed71f32015-12-11 15:49:07 -08001281 sbn = null;
Chris Wren24fb8942015-06-18 14:33:56 -04001282 }
Daniel Sandlerf5a78382015-05-15 23:59:36 -04001283
Christoph Studer05ad4822014-05-16 14:16:03 +02001284 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001285 synchronized (mLock) {
1286 applyUpdateLocked(update);
1287 if (sbn != null) {
1288 SomeArgs args = SomeArgs.obtain();
1289 args.arg1 = sbn;
1290 args.arg2 = mRankingMap;
1291 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,
1292 args).sendToTarget();
1293 } else {
1294 // still pass along the ranking map, it may contain other information
1295 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
1296 mRankingMap).sendToTarget();
Chris Wrenf9536642014-04-17 10:01:54 -04001297 }
John Spurlockc133ab82013-06-10 15:16:22 -04001298 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001299
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001300 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001301
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001302 @Override
Griff Hazen84a00ea2014-09-02 17:10:47 -07001303 public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,
Julia Reynolds503ed942017-10-04 16:04:56 -04001304 NotificationRankingUpdate update, NotificationStats stats, int reason) {
Griff Hazen84a00ea2014-09-02 17:10:47 -07001305 StatusBarNotification sbn;
1306 try {
1307 sbn = sbnHolder.get();
1308 } catch (RemoteException e) {
1309 Log.w(TAG, "onNotificationRemoved: Error receiving StatusBarNotification", e);
1310 return;
1311 }
Christoph Studer05ad4822014-05-16 14:16:03 +02001312 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001313 synchronized (mLock) {
1314 applyUpdateLocked(update);
1315 SomeArgs args = SomeArgs.obtain();
1316 args.arg1 = sbn;
1317 args.arg2 = mRankingMap;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05001318 args.arg3 = reason;
Julia Reynolds503ed942017-10-04 16:04:56 -04001319 args.arg4 = stats;
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001320 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED,
1321 args).sendToTarget();
John Spurlockc133ab82013-06-10 15:16:22 -04001322 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001323
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001324 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001325
John Spurlocka4294292014-03-24 18:02:32 -04001326 @Override
Christoph Studer05ad4822014-05-16 14:16:03 +02001327 public void onListenerConnected(NotificationRankingUpdate update) {
1328 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001329 synchronized (mLock) {
1330 applyUpdateLocked(update);
John Spurlocka4294292014-03-24 18:02:32 -04001331 }
Chris Wren5717bd62016-04-06 18:15:46 -04001332 isConnected = true;
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001333 mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_CONNECTED).sendToTarget();
John Spurlocka4294292014-03-24 18:02:32 -04001334 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001335
Chris Wrenf9536642014-04-17 10:01:54 -04001336 @Override
Christoph Studer05ad4822014-05-16 14:16:03 +02001337 public void onNotificationRankingUpdate(NotificationRankingUpdate update)
Chris Wrenf9536642014-04-17 10:01:54 -04001338 throws RemoteException {
Christoph Studer05ad4822014-05-16 14:16:03 +02001339 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001340 synchronized (mLock) {
1341 applyUpdateLocked(update);
1342 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
1343 mRankingMap).sendToTarget();
Chris Wrenf9536642014-04-17 10:01:54 -04001344 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001345
Chris Wrenf9536642014-04-17 10:01:54 -04001346 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001347
John Spurlock1fa865f2014-07-21 14:56:39 -04001348 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001349 public void onListenerHintsChanged(int hints) throws RemoteException {
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001350 mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_HINTS_CHANGED,
1351 hints, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -04001352 }
Christoph Studer85a384b2014-08-27 20:16:15 +02001353
1354 @Override
1355 public void onInterruptionFilterChanged(int interruptionFilter) throws RemoteException {
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001356 mHandler.obtainMessage(MyHandler.MSG_ON_INTERRUPTION_FILTER_CHANGED,
1357 interruptionFilter, 0).sendToTarget();
Christoph Studer85a384b2014-08-27 20:16:15 +02001358 }
Chris Wren51017d02015-12-15 15:34:46 -05001359
1360 @Override
Julia Reynolds901bf282018-08-14 10:09:36 -04001361 public void onNotificationEnqueuedWithChannel(
1362 IStatusBarNotificationHolder notificationHolder, NotificationChannel channel)
Julia Reynoldsceecfcf2017-01-31 09:44:26 -05001363 throws RemoteException {
Chris Wren51017d02015-12-15 15:34:46 -05001364 // no-op in the listener
1365 }
1366
1367 @Override
Julia Reynolds6a63d1b2018-08-14 16:59:33 -04001368 public void onNotificationsSeen(List<String> keys)
1369 throws RemoteException {
1370 // no-op in the listener
1371 }
1372
1373 @Override
Julia Reynolds79672302017-01-12 08:30:16 -05001374 public void onNotificationSnoozedUntilContext(
1375 IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId)
Chris Wren51017d02015-12-15 15:34:46 -05001376 throws RemoteException {
1377 // no-op in the listener
1378 }
1379
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001380 @Override
Tony Makeda84a72018-11-19 17:01:32 +00001381 public void onNotificationExpansionChanged(
1382 String key, boolean isUserAction, boolean isExpanded) {
1383 // no-op in the listener
1384 }
1385
1386 @Override
1387 public void onNotificationDirectReply(String key) {
1388 // no-op in the listener
1389 }
1390
1391 @Override
Tony Mak29996702018-11-26 16:23:34 +00001392 public void onSuggestedReplySent(String key, CharSequence reply, int source) {
1393 // no-op in the listener
1394 }
1395
1396 @Override
Tony Mak7d4b3a52018-11-27 17:29:36 +00001397 public void onActionClicked(String key, Notification.Action action, int source) {
1398 // no-op in the listener
1399 }
1400
1401 @Override
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001402 public void onNotificationChannelModification(String pkgName, UserHandle user,
1403 NotificationChannel channel,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001404 @ChannelOrGroupModificationTypes int modificationType) {
1405 SomeArgs args = SomeArgs.obtain();
1406 args.arg1 = pkgName;
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001407 args.arg2 = user;
1408 args.arg3 = channel;
1409 args.arg4 = modificationType;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001410 mHandler.obtainMessage(
1411 MyHandler.MSG_ON_NOTIFICATION_CHANNEL_MODIFIED, args).sendToTarget();
1412 }
1413
1414 @Override
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001415 public void onNotificationChannelGroupModification(String pkgName, UserHandle user,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001416 NotificationChannelGroup group,
1417 @ChannelOrGroupModificationTypes int modificationType) {
1418 SomeArgs args = SomeArgs.obtain();
1419 args.arg1 = pkgName;
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001420 args.arg2 = user;
1421 args.arg3 = group;
1422 args.arg4 = modificationType;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001423 mHandler.obtainMessage(
1424 MyHandler.MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED, args).sendToTarget();
1425 }
Julia Reynolds12ad7ca2019-01-28 09:29:16 -05001426
1427 @Override
1428 public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
1429 mHandler.obtainMessage(MyHandler.MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED,
1430 hideSilentStatusIcons).sendToTarget();
1431 }
Chris Wrenf9536642014-04-17 10:01:54 -04001432 }
1433
Julia Reynolds22f02b32016-12-01 15:05:13 -05001434 /**
1435 * @hide
1436 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08001437 @GuardedBy("mLock")
Julia Reynolds22f02b32016-12-01 15:05:13 -05001438 public final void applyUpdateLocked(NotificationRankingUpdate update) {
Christoph Studerd0694b62014-06-04 16:36:01 +02001439 mRankingMap = new RankingMap(update);
1440 }
1441
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001442 /** @hide */
1443 protected Context getContext() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02001444 if (mSystemContext != null) {
1445 return mSystemContext;
1446 }
1447 return this;
1448 }
1449
Christoph Studerd0694b62014-06-04 16:36:01 +02001450 /**
Christoph Studer1d599da2014-06-12 15:25:59 +02001451 * Stores ranking related information on a currently active notification.
Christoph Studerd0694b62014-06-04 16:36:01 +02001452 *
1453 * <p>
Christoph Studer1d599da2014-06-12 15:25:59 +02001454 * Ranking objects aren't automatically updated as notification events
1455 * occur. Instead, ranking information has to be retrieved again via the
1456 * current {@link RankingMap}.
Christoph Studerd0694b62014-06-04 16:36:01 +02001457 */
1458 public static class Ranking {
Julia Reynolds0edb50c2016-02-26 14:08:25 -05001459
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001460 /** Value signifying that the user has not expressed a per-app visibility override value.
1461 * @hide */
Chris Wren5ab5c742016-05-10 15:32:23 -04001462 public static final int VISIBILITY_NO_OVERRIDE = NotificationManager.VISIBILITY_NO_OVERRIDE;
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001463
Julia Reynolds503ed942017-10-04 16:04:56 -04001464 /**
1465 * The user is likely to have a negative reaction to this notification.
1466 */
1467 public static final int USER_SENTIMENT_NEGATIVE = -1;
1468 /**
1469 * It is not known how the user will react to this notification.
1470 */
1471 public static final int USER_SENTIMENT_NEUTRAL = 0;
1472 /**
1473 * The user is likely to have a positive reaction to this notification.
1474 */
1475 public static final int USER_SENTIMENT_POSITIVE = 1;
1476
1477 /** @hide */
1478 @IntDef(prefix = { "USER_SENTIMENT_" }, value = {
1479 USER_SENTIMENT_NEGATIVE, USER_SENTIMENT_NEUTRAL, USER_SENTIMENT_POSITIVE
1480 })
1481 @Retention(RetentionPolicy.SOURCE)
1482 public @interface UserSentiment {}
1483
Christoph Studer1d599da2014-06-12 15:25:59 +02001484 private String mKey;
1485 private int mRank = -1;
1486 private boolean mIsAmbient;
Christoph Studerce7d6d22014-08-26 19:21:31 +02001487 private boolean mMatchesInterruptionFilter;
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001488 private int mVisibilityOverride;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001489 private int mSuppressedVisualEffects;
Chris Wren5ab5c742016-05-10 15:32:23 -04001490 private @NotificationManager.Importance int mImportance;
Chris Wrenbdf33762015-12-04 15:50:51 -05001491 private CharSequence mImportanceExplanation;
Julia Reynoldse46bb372016-03-17 11:05:58 -04001492 // System specified group key.
1493 private String mOverrideGroupKey;
Julia Reynolds22f02b32016-12-01 15:05:13 -05001494 // Notification assistant channel override.
Julia Reynolds924eed12017-01-19 09:52:07 -05001495 private NotificationChannel mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -05001496 // Notification assistant people override.
1497 private ArrayList<String> mOverridePeople;
1498 // Notification assistant snooze criteria.
1499 private ArrayList<SnoozeCriterion> mSnoozeCriteria;
Julia Reynolds924eed12017-01-19 09:52:07 -05001500 private boolean mShowBadge;
Julia Reynolds503ed942017-10-04 16:04:56 -04001501 private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
Beverly5a20a5e2018-03-06 15:02:44 -05001502 private boolean mHidden;
Gus Prevas7306b902018-12-11 10:57:06 -05001503 private long mLastAudiblyAlertedMs;
Gus Prevas9abc5062018-10-31 16:11:04 -04001504 private boolean mNoisy;
Tony Mak628cb932018-06-19 18:30:41 +01001505 private ArrayList<Notification.Action> mSmartActions;
Tony Makc9acf672018-07-20 13:58:24 +02001506 private ArrayList<CharSequence> mSmartReplies;
Julia Reynolds4509ce72019-01-31 13:12:43 -05001507 private boolean mCanBubble;
Christoph Studerd0694b62014-06-04 16:36:01 +02001508
Christoph Studer1d599da2014-06-12 15:25:59 +02001509 public Ranking() {}
Christoph Studerd0694b62014-06-04 16:36:01 +02001510
1511 /**
1512 * Returns the key of the notification this Ranking applies to.
1513 */
1514 public String getKey() {
1515 return mKey;
1516 }
1517
1518 /**
1519 * Returns the rank of the notification.
1520 *
1521 * @return the rank of the notification, that is the 0-based index in
1522 * the list of active notifications.
1523 */
1524 public int getRank() {
1525 return mRank;
1526 }
1527
1528 /**
1529 * Returns whether the notification is an ambient notification, that is
1530 * a notification that doesn't require the user's immediate attention.
1531 */
1532 public boolean isAmbient() {
1533 return mIsAmbient;
1534 }
1535
1536 /**
Julia Reynolds924eed12017-01-19 09:52:07 -05001537 * Returns the user specified visibility for the package that posted
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001538 * this notification, or
1539 * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if
1540 * no such preference has been expressed.
1541 * @hide
1542 */
Mathew Inwoode3807372018-08-10 09:51:03 +01001543 @UnsupportedAppUsage
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001544 public int getVisibilityOverride() {
1545 return mVisibilityOverride;
1546 }
1547
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001548 /**
1549 * Returns the type(s) of visual effects that should be suppressed for this notification.
Julia Reynoldsccc6ae62018-03-01 16:24:49 -05001550 * See {@link NotificationManager.Policy}, e.g.
1551 * {@link NotificationManager.Policy#SUPPRESSED_EFFECT_LIGHTS}.
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001552 */
1553 public int getSuppressedVisualEffects() {
1554 return mSuppressedVisualEffects;
1555 }
1556
Christoph Studerce7d6d22014-08-26 19:21:31 +02001557 /**
1558 * Returns whether the notification matches the user's interruption
1559 * filter.
Chris Wren0fef44d2014-09-30 13:05:14 -04001560 *
1561 * @return {@code true} if the notification is allowed by the filter, or
1562 * {@code false} if it is blocked.
Christoph Studerce7d6d22014-08-26 19:21:31 +02001563 */
1564 public boolean matchesInterruptionFilter() {
1565 return mMatchesInterruptionFilter;
Christoph Studer1d599da2014-06-12 15:25:59 +02001566 }
1567
Chris Wren9fa689f2015-11-20 16:44:53 -05001568 /**
1569 * Returns the importance of the notification, which dictates its
Chris Wren5ab5c742016-05-10 15:32:23 -04001570 * modes of presentation, see: {@link NotificationManager#IMPORTANCE_DEFAULT}, etc.
Chris Wren9fa689f2015-11-20 16:44:53 -05001571 *
Julia Reynolds924eed12017-01-19 09:52:07 -05001572 * @return the importance of the notification
Chris Wren9fa689f2015-11-20 16:44:53 -05001573 */
Chris Wren5ab5c742016-05-10 15:32:23 -04001574 public @NotificationManager.Importance int getImportance() {
Chris Wrenbdf33762015-12-04 15:50:51 -05001575 return mImportance;
Chris Wren9fa689f2015-11-20 16:44:53 -05001576 }
1577
1578 /**
Julia Reynolds22f02b32016-12-01 15:05:13 -05001579 * If the importance has been overridden by user preference, then this will be non-null,
Chris Wren9fa689f2015-11-20 16:44:53 -05001580 * and should be displayed to the user.
1581 *
1582 * @return the explanation for the importance, or null if it is the natural importance
1583 */
1584 public CharSequence getImportanceExplanation() {
Chris Wrenbdf33762015-12-04 15:50:51 -05001585 return mImportanceExplanation;
Chris Wren9fa689f2015-11-20 16:44:53 -05001586 }
1587
Julia Reynoldse46bb372016-03-17 11:05:58 -04001588 /**
Julia Reynolds22f02b32016-12-01 15:05:13 -05001589 * If the system has overridden the group key, then this will be non-null, and this
Julia Reynoldse46bb372016-03-17 11:05:58 -04001590 * key should be used to bundle notifications.
1591 */
1592 public String getOverrideGroupKey() {
1593 return mOverrideGroupKey;
1594 }
1595
Julia Reynolds22f02b32016-12-01 15:05:13 -05001596 /**
Julia Reynolds924eed12017-01-19 09:52:07 -05001597 * Returns the notification channel this notification was posted to, which dictates
1598 * notification behavior and presentation.
Julia Reynolds22f02b32016-12-01 15:05:13 -05001599 */
1600 public NotificationChannel getChannel() {
Julia Reynolds924eed12017-01-19 09:52:07 -05001601 return mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -05001602 }
1603
1604 /**
Julia Reynolds503ed942017-10-04 16:04:56 -04001605 * Returns how the system thinks the user feels about notifications from the
1606 * channel provided by {@link #getChannel()}. You can use this information to expose
1607 * controls to help the user block this channel's notifications, if the sentiment is
1608 * {@link #USER_SENTIMENT_NEGATIVE}, or emphasize this notification if the sentiment is
1609 * {@link #USER_SENTIMENT_POSITIVE}.
1610 */
1611 public int getUserSentiment() {
1612 return mUserSentiment;
1613 }
1614
1615 /**
Julia Reynolds22f02b32016-12-01 15:05:13 -05001616 * If the {@link NotificationAssistantService} has added people to this notification, then
1617 * this will be non-null.
Julia Reynolds1327d3c2017-02-17 09:26:45 -05001618 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -04001619 * @removed
Julia Reynolds22f02b32016-12-01 15:05:13 -05001620 */
Julia Reynolds1327d3c2017-02-17 09:26:45 -05001621 @SystemApi
Julia Reynolds22f02b32016-12-01 15:05:13 -05001622 public List<String> getAdditionalPeople() {
1623 return mOverridePeople;
1624 }
1625
1626 /**
1627 * Returns snooze criteria provided by the {@link NotificationAssistantService}. If your
1628 * user interface displays options for snoozing notifications these criteria should be
1629 * displayed as well.
Julia Reynolds1327d3c2017-02-17 09:26:45 -05001630 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -04001631 * @removed
Julia Reynolds22f02b32016-12-01 15:05:13 -05001632 */
Julia Reynolds1327d3c2017-02-17 09:26:45 -05001633 @SystemApi
Julia Reynolds22f02b32016-12-01 15:05:13 -05001634 public List<SnoozeCriterion> getSnoozeCriteria() {
1635 return mSnoozeCriteria;
1636 }
1637
Julia Reynolds924eed12017-01-19 09:52:07 -05001638 /**
Fabian Kozynskib0da4bc2019-01-15 17:44:27 -05001639 * Returns a list of smart {@link Notification.Action} that can be added by the
1640 * {@link NotificationAssistantService}
Tony Mak628cb932018-06-19 18:30:41 +01001641 */
Fabian Kozynski867550e2019-02-28 12:59:57 -05001642 public @NonNull List<Notification.Action> getSmartActions() {
Tony Mak628cb932018-06-19 18:30:41 +01001643 return mSmartActions;
1644 }
1645
1646 /**
Fabian Kozynskib0da4bc2019-01-15 17:44:27 -05001647 * Returns a list of smart replies that can be added by the
1648 * {@link NotificationAssistantService}
Tony Makc9acf672018-07-20 13:58:24 +02001649 */
Fabian Kozynski867550e2019-02-28 12:59:57 -05001650 public @NonNull List<CharSequence> getSmartReplies() {
Tony Makc9acf672018-07-20 13:58:24 +02001651 return mSmartReplies;
1652 }
1653
1654 /**
Julia Reynolds924eed12017-01-19 09:52:07 -05001655 * Returns whether this notification can be displayed as a badge.
1656 *
1657 * @return true if the notification can be displayed as a badge, false otherwise.
1658 */
1659 public boolean canShowBadge() {
1660 return mShowBadge;
1661 }
1662
Dan Sandler1d958f82018-01-09 21:10:26 -05001663 /**
Beverly5a20a5e2018-03-06 15:02:44 -05001664 * Returns whether the app that posted this notification is suspended, so this notification
1665 * should be hidden.
1666 *
1667 * @return true if the notification should be hidden, false otherwise.
1668 */
1669 public boolean isSuspended() {
1670 return mHidden;
1671 }
1672
1673 /**
Gus Prevas7306b902018-12-11 10:57:06 -05001674 * Returns the last time this notification alerted the user via sound or vibration.
Gus Prevasa3226492018-10-23 11:10:09 -04001675 *
Gus Prevas7306b902018-12-11 10:57:06 -05001676 * @return the time of the last alerting behavior, in milliseconds.
Gus Prevasa3226492018-10-23 11:10:09 -04001677 */
Gus Prevas7306b902018-12-11 10:57:06 -05001678 public long getLastAudiblyAlertedMillis() {
1679 return mLastAudiblyAlertedMs;
Gus Prevasa3226492018-10-23 11:10:09 -04001680 }
1681
Julia Reynolds4509ce72019-01-31 13:12:43 -05001682 /**
1683 * Returns whether the user has allowed bubbles globally, at the app level, and at the
1684 * channel level for this notification.
1685 *
1686 * <p>This does not take into account the current importance of the notification, the
1687 * current DND state, or whether the posting app is foreground.</p>
1688 */
1689 public boolean canBubble() {
1690 return mCanBubble;
1691 }
1692
Gus Prevas9abc5062018-10-31 16:11:04 -04001693 /** @hide */
1694 public boolean isNoisy() {
1695 return mNoisy;
1696 }
1697
Gus Prevasa3226492018-10-23 11:10:09 -04001698 /**
Dan Sandler1d958f82018-01-09 21:10:26 -05001699 * @hide
1700 */
1701 @VisibleForTesting
1702 public void populate(String key, int rank, boolean matchesInterruptionFilter,
Julia Reynolds0421e6d2016-01-08 09:51:24 -05001703 int visibilityOverride, int suppressedVisualEffects, int importance,
Julia Reynolds22f02b32016-12-01 15:05:13 -05001704 CharSequence explanation, String overrideGroupKey,
Julia Reynolds924eed12017-01-19 09:52:07 -05001705 NotificationChannel channel, ArrayList<String> overridePeople,
Julia Reynolds503ed942017-10-04 16:04:56 -04001706 ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
Gus Prevas7306b902018-12-11 10:57:06 -05001707 int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
Gus Prevas9abc5062018-10-31 16:11:04 -04001708 boolean noisy, ArrayList<Notification.Action> smartActions,
Julia Reynolds4509ce72019-01-31 13:12:43 -05001709 ArrayList<CharSequence> smartReplies, boolean canBubble) {
Christoph Studer1d599da2014-06-12 15:25:59 +02001710 mKey = key;
1711 mRank = rank;
Julia Reynolds85769912016-10-25 09:08:57 -04001712 mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
Christoph Studerce7d6d22014-08-26 19:21:31 +02001713 mMatchesInterruptionFilter = matchesInterruptionFilter;
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001714 mVisibilityOverride = visibilityOverride;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001715 mSuppressedVisualEffects = suppressedVisualEffects;
Chris Wrenbdf33762015-12-04 15:50:51 -05001716 mImportance = importance;
1717 mImportanceExplanation = explanation;
Julia Reynoldse46bb372016-03-17 11:05:58 -04001718 mOverrideGroupKey = overrideGroupKey;
Julia Reynolds924eed12017-01-19 09:52:07 -05001719 mChannel = channel;
Julia Reynolds22f02b32016-12-01 15:05:13 -05001720 mOverridePeople = overridePeople;
1721 mSnoozeCriteria = snoozeCriteria;
Julia Reynolds924eed12017-01-19 09:52:07 -05001722 mShowBadge = showBadge;
Julia Reynolds503ed942017-10-04 16:04:56 -04001723 mUserSentiment = userSentiment;
Beverly5a20a5e2018-03-06 15:02:44 -05001724 mHidden = hidden;
Gus Prevas7306b902018-12-11 10:57:06 -05001725 mLastAudiblyAlertedMs = lastAudiblyAlertedMs;
Gus Prevas9abc5062018-10-31 16:11:04 -04001726 mNoisy = noisy;
Tony Mak628cb932018-06-19 18:30:41 +01001727 mSmartActions = smartActions;
Tony Makc9acf672018-07-20 13:58:24 +02001728 mSmartReplies = smartReplies;
Julia Reynolds4509ce72019-01-31 13:12:43 -05001729 mCanBubble = canBubble;
Christoph Studerd0694b62014-06-04 16:36:01 +02001730 }
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001731
1732 /**
1733 * {@hide}
1734 */
1735 public static String importanceToString(int importance) {
1736 switch (importance) {
Julia Reynolds85769912016-10-25 09:08:57 -04001737 case NotificationManager.IMPORTANCE_UNSPECIFIED:
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001738 return "UNSPECIFIED";
Julia Reynolds85769912016-10-25 09:08:57 -04001739 case NotificationManager.IMPORTANCE_NONE:
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001740 return "NONE";
Julia Reynolds85769912016-10-25 09:08:57 -04001741 case NotificationManager.IMPORTANCE_MIN:
Julia Reynoldsf0f629f2016-02-25 09:34:04 -05001742 return "MIN";
Julia Reynolds85769912016-10-25 09:08:57 -04001743 case NotificationManager.IMPORTANCE_LOW:
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001744 return "LOW";
Julia Reynolds85769912016-10-25 09:08:57 -04001745 case NotificationManager.IMPORTANCE_DEFAULT:
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001746 return "DEFAULT";
Julia Reynolds85769912016-10-25 09:08:57 -04001747 case NotificationManager.IMPORTANCE_HIGH:
1748 case NotificationManager.IMPORTANCE_MAX:
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001749 return "HIGH";
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001750 default:
1751 return "UNKNOWN(" + String.valueOf(importance) + ")";
1752 }
1753 }
Christoph Studer05ad4822014-05-16 14:16:03 +02001754 }
1755
1756 /**
1757 * Provides access to ranking information on currently active
1758 * notifications.
1759 *
1760 * <p>
1761 * Note that this object represents a ranking snapshot that only applies to
1762 * notifications active at the time of retrieval.
1763 */
Christoph Studerd0694b62014-06-04 16:36:01 +02001764 public static class RankingMap implements Parcelable {
Christoph Studer05ad4822014-05-16 14:16:03 +02001765 private final NotificationRankingUpdate mRankingUpdate;
Christoph Studerdda48f12014-07-29 23:13:16 +02001766 private ArrayMap<String,Integer> mRanks;
1767 private ArraySet<Object> mIntercepted;
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001768 private ArrayMap<String, Integer> mVisibilityOverrides;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001769 private ArrayMap<String, Integer> mSuppressedVisualEffects;
Chris Wrenbdf33762015-12-04 15:50:51 -05001770 private ArrayMap<String, Integer> mImportance;
1771 private ArrayMap<String, String> mImportanceExplanation;
Julia Reynoldse46bb372016-03-17 11:05:58 -04001772 private ArrayMap<String, String> mOverrideGroupKeys;
Julia Reynolds924eed12017-01-19 09:52:07 -05001773 private ArrayMap<String, NotificationChannel> mChannels;
Julia Reynolds22f02b32016-12-01 15:05:13 -05001774 private ArrayMap<String, ArrayList<String>> mOverridePeople;
1775 private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
Julia Reynolds924eed12017-01-19 09:52:07 -05001776 private ArrayMap<String, Boolean> mShowBadge;
Julia Reynolds503ed942017-10-04 16:04:56 -04001777 private ArrayMap<String, Integer> mUserSentiment;
Beverly5a20a5e2018-03-06 15:02:44 -05001778 private ArrayMap<String, Boolean> mHidden;
Gus Prevas7306b902018-12-11 10:57:06 -05001779 private ArrayMap<String, Long> mLastAudiblyAlerted;
Gus Prevas9abc5062018-10-31 16:11:04 -04001780 private ArrayMap<String, Boolean> mNoisy;
Tony Mak628cb932018-06-19 18:30:41 +01001781 private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions;
Tony Makc9acf672018-07-20 13:58:24 +02001782 private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies;
Julia Reynolds4509ce72019-01-31 13:12:43 -05001783 private boolean[] mCanBubble;
Christoph Studer05ad4822014-05-16 14:16:03 +02001784
Christoph Studerd0694b62014-06-04 16:36:01 +02001785 private RankingMap(NotificationRankingUpdate rankingUpdate) {
Christoph Studer05ad4822014-05-16 14:16:03 +02001786 mRankingUpdate = rankingUpdate;
1787 }
1788
1789 /**
1790 * Request the list of notification keys in their current ranking
1791 * order.
1792 *
1793 * @return An array of active notification keys, in their ranking order.
1794 */
1795 public String[] getOrderedKeys() {
1796 return mRankingUpdate.getOrderedKeys();
1797 }
1798
1799 /**
Christoph Studer1d599da2014-06-12 15:25:59 +02001800 * Populates outRanking with ranking information for the notification
1801 * with the given key.
Christoph Studer05ad4822014-05-16 14:16:03 +02001802 *
Christoph Studer1d599da2014-06-12 15:25:59 +02001803 * @return true if a valid key has been passed and outRanking has
1804 * been populated; false otherwise
Christoph Studer05ad4822014-05-16 14:16:03 +02001805 */
Christoph Studer1d599da2014-06-12 15:25:59 +02001806 public boolean getRanking(String key, Ranking outRanking) {
1807 int rank = getRank(key);
Julia Reynolds0421e6d2016-01-08 09:51:24 -05001808 outRanking.populate(key, rank, !isIntercepted(key),
Chris Wrenbdf33762015-12-04 15:50:51 -05001809 getVisibilityOverride(key), getSuppressedVisualEffects(key),
Julia Reynolds22f02b32016-12-01 15:05:13 -05001810 getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
Julia Reynolds924eed12017-01-19 09:52:07 -05001811 getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
Gus Prevasa3226492018-10-23 11:10:09 -04001812 getShowBadge(key), getUserSentiment(key), getHidden(key),
Gus Prevas7306b902018-12-11 10:57:06 -05001813 getLastAudiblyAlerted(key), getNoisy(key), getSmartActions(key),
Julia Reynolds4509ce72019-01-31 13:12:43 -05001814 getSmartReplies(key), canBubble(key));
Christoph Studer1d599da2014-06-12 15:25:59 +02001815 return rank >= 0;
Christoph Studerd0694b62014-06-04 16:36:01 +02001816 }
1817
Christoph Studer1d599da2014-06-12 15:25:59 +02001818 private int getRank(String key) {
Christoph Studerdda48f12014-07-29 23:13:16 +02001819 synchronized (this) {
1820 if (mRanks == null) {
1821 buildRanksLocked();
Christoph Studer05ad4822014-05-16 14:16:03 +02001822 }
1823 }
Christoph Studerdda48f12014-07-29 23:13:16 +02001824 Integer rank = mRanks.get(key);
1825 return rank != null ? rank : -1;
Christoph Studer1d599da2014-06-12 15:25:59 +02001826 }
1827
Christoph Studer1d599da2014-06-12 15:25:59 +02001828 private boolean isIntercepted(String key) {
Christoph Studerdda48f12014-07-29 23:13:16 +02001829 synchronized (this) {
1830 if (mIntercepted == null) {
1831 buildInterceptedSetLocked();
Christoph Studer1d599da2014-06-12 15:25:59 +02001832 }
1833 }
Christoph Studerdda48f12014-07-29 23:13:16 +02001834 return mIntercepted.contains(key);
1835 }
1836
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001837 private int getVisibilityOverride(String key) {
1838 synchronized (this) {
1839 if (mVisibilityOverrides == null) {
1840 buildVisibilityOverridesLocked();
1841 }
1842 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001843 Integer override = mVisibilityOverrides.get(key);
1844 if (override == null) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001845 return Ranking.VISIBILITY_NO_OVERRIDE;
1846 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001847 return override.intValue();
1848 }
1849
1850 private int getSuppressedVisualEffects(String key) {
1851 synchronized (this) {
1852 if (mSuppressedVisualEffects == null) {
1853 buildSuppressedVisualEffectsLocked();
1854 }
1855 }
1856 Integer suppressed = mSuppressedVisualEffects.get(key);
1857 if (suppressed == null) {
1858 return 0;
1859 }
1860 return suppressed.intValue();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001861 }
1862
Chris Wrenbdf33762015-12-04 15:50:51 -05001863 private int getImportance(String key) {
1864 synchronized (this) {
1865 if (mImportance == null) {
1866 buildImportanceLocked();
1867 }
1868 }
1869 Integer importance = mImportance.get(key);
1870 if (importance == null) {
Julia Reynolds85769912016-10-25 09:08:57 -04001871 return NotificationManager.IMPORTANCE_DEFAULT;
Chris Wrenbdf33762015-12-04 15:50:51 -05001872 }
1873 return importance.intValue();
1874 }
1875
1876 private String getImportanceExplanation(String key) {
1877 synchronized (this) {
1878 if (mImportanceExplanation == null) {
1879 buildImportanceExplanationLocked();
1880 }
1881 }
1882 return mImportanceExplanation.get(key);
1883 }
1884
Julia Reynoldse46bb372016-03-17 11:05:58 -04001885 private String getOverrideGroupKey(String key) {
1886 synchronized (this) {
1887 if (mOverrideGroupKeys == null) {
1888 buildOverrideGroupKeys();
1889 }
1890 }
1891 return mOverrideGroupKeys.get(key);
1892 }
1893
Julia Reynolds924eed12017-01-19 09:52:07 -05001894 private NotificationChannel getChannel(String key) {
Julia Reynolds22f02b32016-12-01 15:05:13 -05001895 synchronized (this) {
Julia Reynolds924eed12017-01-19 09:52:07 -05001896 if (mChannels == null) {
1897 buildChannelsLocked();
Julia Reynolds22f02b32016-12-01 15:05:13 -05001898 }
1899 }
Julia Reynolds924eed12017-01-19 09:52:07 -05001900 return mChannels.get(key);
Julia Reynolds22f02b32016-12-01 15:05:13 -05001901 }
1902
1903 private ArrayList<String> getOverridePeople(String key) {
1904 synchronized (this) {
1905 if (mOverridePeople == null) {
1906 buildOverridePeopleLocked();
1907 }
1908 }
1909 return mOverridePeople.get(key);
1910 }
1911
1912 private ArrayList<SnoozeCriterion> getSnoozeCriteria(String key) {
1913 synchronized (this) {
1914 if (mSnoozeCriteria == null) {
1915 buildSnoozeCriteriaLocked();
1916 }
1917 }
1918 return mSnoozeCriteria.get(key);
1919 }
1920
Julia Reynolds924eed12017-01-19 09:52:07 -05001921 private boolean getShowBadge(String key) {
1922 synchronized (this) {
1923 if (mShowBadge == null) {
1924 buildShowBadgeLocked();
1925 }
1926 }
1927 Boolean showBadge = mShowBadge.get(key);
1928 return showBadge == null ? false : showBadge.booleanValue();
1929 }
1930
Julia Reynolds503ed942017-10-04 16:04:56 -04001931 private int getUserSentiment(String key) {
1932 synchronized (this) {
1933 if (mUserSentiment == null) {
1934 buildUserSentimentLocked();
1935 }
1936 }
1937 Integer userSentiment = mUserSentiment.get(key);
1938 return userSentiment == null
1939 ? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue();
1940 }
1941
Beverly5a20a5e2018-03-06 15:02:44 -05001942 private boolean getHidden(String key) {
1943 synchronized (this) {
1944 if (mHidden == null) {
1945 buildHiddenLocked();
1946 }
1947 }
1948 Boolean hidden = mHidden.get(key);
1949 return hidden == null ? false : hidden.booleanValue();
1950 }
1951
Gus Prevas7306b902018-12-11 10:57:06 -05001952 private long getLastAudiblyAlerted(String key) {
Gus Prevasa3226492018-10-23 11:10:09 -04001953 synchronized (this) {
Gus Prevas7306b902018-12-11 10:57:06 -05001954 if (mLastAudiblyAlerted == null) {
1955 buildLastAudiblyAlertedLocked();
Gus Prevasa3226492018-10-23 11:10:09 -04001956 }
1957 }
Gus Prevas7306b902018-12-11 10:57:06 -05001958 Long lastAudibleAlerted = mLastAudiblyAlerted.get(key);
1959 return lastAudibleAlerted == null ? -1 : lastAudibleAlerted.longValue();
Gus Prevasa3226492018-10-23 11:10:09 -04001960 }
1961
Gus Prevas9abc5062018-10-31 16:11:04 -04001962 private boolean getNoisy(String key) {
1963 synchronized (this) {
1964 if (mNoisy == null) {
1965 buildNoisyLocked();
1966 }
1967 }
1968 Boolean noisy = mNoisy.get(key);
1969 return noisy == null ? false : noisy.booleanValue();
1970 }
1971
Tony Mak628cb932018-06-19 18:30:41 +01001972 private ArrayList<Notification.Action> getSmartActions(String key) {
1973 synchronized (this) {
1974 if (mSmartActions == null) {
1975 buildSmartActions();
1976 }
1977 }
1978 return mSmartActions.get(key);
1979 }
1980
Tony Makc9acf672018-07-20 13:58:24 +02001981 private ArrayList<CharSequence> getSmartReplies(String key) {
1982 synchronized (this) {
1983 if (mSmartReplies == null) {
1984 buildSmartReplies();
1985 }
1986 }
1987 return mSmartReplies.get(key);
1988 }
1989
Julia Reynolds4509ce72019-01-31 13:12:43 -05001990 private boolean canBubble(String key) {
1991 synchronized (this) {
1992 if (mRanks == null) {
1993 buildRanksLocked();
1994 }
1995 if (mCanBubble == null) {
1996 mCanBubble = mRankingUpdate.getCanBubble();
1997 }
1998 }
1999 int keyIndex = mRanks.getOrDefault(key, -1);
2000 return keyIndex >= 0 ? mCanBubble[keyIndex] : false;
2001 }
2002
Christoph Studerdda48f12014-07-29 23:13:16 +02002003 // Locked by 'this'
2004 private void buildRanksLocked() {
2005 String[] orderedKeys = mRankingUpdate.getOrderedKeys();
2006 mRanks = new ArrayMap<>(orderedKeys.length);
2007 for (int i = 0; i < orderedKeys.length; i++) {
2008 String key = orderedKeys[i];
2009 mRanks.put(key, i);
2010 }
2011 }
2012
2013 // Locked by 'this'
2014 private void buildInterceptedSetLocked() {
2015 String[] dndInterceptedKeys = mRankingUpdate.getInterceptedKeys();
2016 mIntercepted = new ArraySet<>(dndInterceptedKeys.length);
2017 Collections.addAll(mIntercepted, dndInterceptedKeys);
Christoph Studer05ad4822014-05-16 14:16:03 +02002018 }
2019
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002020 private ArrayMap<String, Integer> buildIntMapFromBundle(Bundle bundle) {
2021 ArrayMap<String, Integer> newMap = new ArrayMap<>(bundle.size());
2022 for (String key : bundle.keySet()) {
2023 newMap.put(key, bundle.getInt(key));
2024 }
2025 return newMap;
2026 }
2027
2028 private ArrayMap<String, String> buildStringMapFromBundle(Bundle bundle) {
2029 ArrayMap<String, String> newMap = new ArrayMap<>(bundle.size());
2030 for (String key : bundle.keySet()) {
2031 newMap.put(key, bundle.getString(key));
2032 }
2033 return newMap;
2034 }
2035
2036 private ArrayMap<String, Boolean> buildBooleanMapFromBundle(Bundle bundle) {
2037 ArrayMap<String, Boolean> newMap = new ArrayMap<>(bundle.size());
2038 for (String key : bundle.keySet()) {
2039 newMap.put(key, bundle.getBoolean(key));
2040 }
2041 return newMap;
2042 }
2043
Gus Prevas7306b902018-12-11 10:57:06 -05002044 private ArrayMap<String, Long> buildLongMapFromBundle(Bundle bundle) {
2045 ArrayMap<String, Long> newMap = new ArrayMap<>(bundle.size());
2046 for (String key : bundle.keySet()) {
2047 newMap.put(key, bundle.getLong(key));
2048 }
2049 return newMap;
2050 }
2051
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002052 // Locked by 'this'
2053 private void buildVisibilityOverridesLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002054 mVisibilityOverrides = buildIntMapFromBundle(mRankingUpdate.getVisibilityOverrides());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002055 }
2056
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002057 // Locked by 'this'
2058 private void buildSuppressedVisualEffectsLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002059 mSuppressedVisualEffects =
2060 buildIntMapFromBundle(mRankingUpdate.getSuppressedVisualEffects());
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002061 }
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002062
Chris Wrenbdf33762015-12-04 15:50:51 -05002063 // Locked by 'this'
2064 private void buildImportanceLocked() {
2065 String[] orderedKeys = mRankingUpdate.getOrderedKeys();
2066 int[] importance = mRankingUpdate.getImportance();
2067 mImportance = new ArrayMap<>(orderedKeys.length);
2068 for (int i = 0; i < orderedKeys.length; i++) {
2069 String key = orderedKeys[i];
2070 mImportance.put(key, importance[i]);
2071 }
2072 }
2073
2074 // Locked by 'this'
2075 private void buildImportanceExplanationLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002076 mImportanceExplanation =
2077 buildStringMapFromBundle(mRankingUpdate.getImportanceExplanation());
Chris Wrenbdf33762015-12-04 15:50:51 -05002078 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002079
Julia Reynoldse46bb372016-03-17 11:05:58 -04002080 // Locked by 'this'
2081 private void buildOverrideGroupKeys() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002082 mOverrideGroupKeys = buildStringMapFromBundle(mRankingUpdate.getOverrideGroupKeys());
Julia Reynoldse46bb372016-03-17 11:05:58 -04002083 }
2084
Julia Reynolds22f02b32016-12-01 15:05:13 -05002085 // Locked by 'this'
Julia Reynolds924eed12017-01-19 09:52:07 -05002086 private void buildChannelsLocked() {
2087 Bundle channels = mRankingUpdate.getChannels();
2088 mChannels = new ArrayMap<>(channels.size());
2089 for (String key : channels.keySet()) {
2090 mChannels.put(key, channels.getParcelable(key));
Julia Reynolds22f02b32016-12-01 15:05:13 -05002091 }
2092 }
2093
2094 // Locked by 'this'
2095 private void buildOverridePeopleLocked() {
2096 Bundle overridePeople = mRankingUpdate.getOverridePeople();
2097 mOverridePeople = new ArrayMap<>(overridePeople.size());
2098 for (String key : overridePeople.keySet()) {
2099 mOverridePeople.put(key, overridePeople.getStringArrayList(key));
2100 }
2101 }
2102
2103 // Locked by 'this'
2104 private void buildSnoozeCriteriaLocked() {
2105 Bundle snoozeCriteria = mRankingUpdate.getSnoozeCriteria();
2106 mSnoozeCriteria = new ArrayMap<>(snoozeCriteria.size());
2107 for (String key : snoozeCriteria.keySet()) {
2108 mSnoozeCriteria.put(key, snoozeCriteria.getParcelableArrayList(key));
2109 }
2110 }
2111
Julia Reynolds924eed12017-01-19 09:52:07 -05002112 // Locked by 'this'
2113 private void buildShowBadgeLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002114 mShowBadge = buildBooleanMapFromBundle(mRankingUpdate.getShowBadge());
Julia Reynolds924eed12017-01-19 09:52:07 -05002115 }
2116
Julia Reynolds503ed942017-10-04 16:04:56 -04002117 // Locked by 'this'
2118 private void buildUserSentimentLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002119 mUserSentiment = buildIntMapFromBundle(mRankingUpdate.getUserSentiment());
Julia Reynolds503ed942017-10-04 16:04:56 -04002120 }
2121
Beverly5a20a5e2018-03-06 15:02:44 -05002122 // Locked by 'this'
2123 private void buildHiddenLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002124 mHidden = buildBooleanMapFromBundle(mRankingUpdate.getHidden());
Beverly5a20a5e2018-03-06 15:02:44 -05002125 }
2126
Tony Mak628cb932018-06-19 18:30:41 +01002127 // Locked by 'this'
Gus Prevas7306b902018-12-11 10:57:06 -05002128 private void buildLastAudiblyAlertedLocked() {
2129 mLastAudiblyAlerted = buildLongMapFromBundle(mRankingUpdate.getLastAudiblyAlerted());
Gus Prevasa3226492018-10-23 11:10:09 -04002130 }
2131
2132 // Locked by 'this'
Gus Prevas9abc5062018-10-31 16:11:04 -04002133 private void buildNoisyLocked() {
2134 mNoisy = buildBooleanMapFromBundle(mRankingUpdate.getNoisy());
2135 }
2136
2137 // Locked by 'this'
Tony Mak628cb932018-06-19 18:30:41 +01002138 private void buildSmartActions() {
2139 Bundle smartActions = mRankingUpdate.getSmartActions();
2140 mSmartActions = new ArrayMap<>(smartActions.size());
2141 for (String key : smartActions.keySet()) {
2142 mSmartActions.put(key, smartActions.getParcelableArrayList(key));
2143 }
2144 }
2145
Tony Makc9acf672018-07-20 13:58:24 +02002146 // Locked by 'this'
2147 private void buildSmartReplies() {
2148 Bundle smartReplies = mRankingUpdate.getSmartReplies();
2149 mSmartReplies = new ArrayMap<>(smartReplies.size());
2150 for (String key : smartReplies.keySet()) {
2151 mSmartReplies.put(key, smartReplies.getCharSequenceArrayList(key));
2152 }
2153 }
2154
Christoph Studer05ad4822014-05-16 14:16:03 +02002155 // ----------- Parcelable
2156
2157 @Override
2158 public int describeContents() {
2159 return 0;
2160 }
2161
2162 @Override
2163 public void writeToParcel(Parcel dest, int flags) {
2164 dest.writeParcelable(mRankingUpdate, flags);
2165 }
2166
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07002167 public static final @android.annotation.NonNull Creator<RankingMap> CREATOR = new Creator<RankingMap>() {
Christoph Studer05ad4822014-05-16 14:16:03 +02002168 @Override
Christoph Studerd0694b62014-06-04 16:36:01 +02002169 public RankingMap createFromParcel(Parcel source) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002170 NotificationRankingUpdate rankingUpdate = source.readParcelable(null);
Christoph Studerd0694b62014-06-04 16:36:01 +02002171 return new RankingMap(rankingUpdate);
Christoph Studer05ad4822014-05-16 14:16:03 +02002172 }
2173
2174 @Override
Christoph Studerd0694b62014-06-04 16:36:01 +02002175 public RankingMap[] newArray(int size) {
2176 return new RankingMap[size];
Christoph Studer05ad4822014-05-16 14:16:03 +02002177 }
2178 };
Daniel Sandler5feceeb2013-03-22 18:29:23 -07002179 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002180
2181 private final class MyHandler extends Handler {
2182 public static final int MSG_ON_NOTIFICATION_POSTED = 1;
2183 public static final int MSG_ON_NOTIFICATION_REMOVED = 2;
2184 public static final int MSG_ON_LISTENER_CONNECTED = 3;
2185 public static final int MSG_ON_NOTIFICATION_RANKING_UPDATE = 4;
2186 public static final int MSG_ON_LISTENER_HINTS_CHANGED = 5;
2187 public static final int MSG_ON_INTERRUPTION_FILTER_CHANGED = 6;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002188 public static final int MSG_ON_NOTIFICATION_CHANNEL_MODIFIED = 7;
2189 public static final int MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED = 8;
Julia Reynolds12ad7ca2019-01-28 09:29:16 -05002190 public static final int MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED = 9;
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002191
2192 public MyHandler(Looper looper) {
2193 super(looper, null, false);
2194 }
2195
2196 @Override
2197 public void handleMessage(Message msg) {
Chris Wren5717bd62016-04-06 18:15:46 -04002198 if (!isConnected) {
2199 return;
2200 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002201 switch (msg.what) {
2202 case MSG_ON_NOTIFICATION_POSTED: {
2203 SomeArgs args = (SomeArgs) msg.obj;
2204 StatusBarNotification sbn = (StatusBarNotification) args.arg1;
2205 RankingMap rankingMap = (RankingMap) args.arg2;
2206 args.recycle();
2207 onNotificationPosted(sbn, rankingMap);
2208 } break;
2209
2210 case MSG_ON_NOTIFICATION_REMOVED: {
2211 SomeArgs args = (SomeArgs) msg.obj;
2212 StatusBarNotification sbn = (StatusBarNotification) args.arg1;
2213 RankingMap rankingMap = (RankingMap) args.arg2;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05002214 int reason = (int) args.arg3;
Julia Reynolds503ed942017-10-04 16:04:56 -04002215 NotificationStats stats = (NotificationStats) args.arg4;
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002216 args.recycle();
Julia Reynolds503ed942017-10-04 16:04:56 -04002217 onNotificationRemoved(sbn, rankingMap, stats, reason);
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002218 } break;
2219
2220 case MSG_ON_LISTENER_CONNECTED: {
2221 onListenerConnected();
2222 } break;
2223
2224 case MSG_ON_NOTIFICATION_RANKING_UPDATE: {
2225 RankingMap rankingMap = (RankingMap) msg.obj;
2226 onNotificationRankingUpdate(rankingMap);
2227 } break;
2228
2229 case MSG_ON_LISTENER_HINTS_CHANGED: {
2230 final int hints = msg.arg1;
2231 onListenerHintsChanged(hints);
2232 } break;
2233
2234 case MSG_ON_INTERRUPTION_FILTER_CHANGED: {
2235 final int interruptionFilter = msg.arg1;
2236 onInterruptionFilterChanged(interruptionFilter);
2237 } break;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002238
2239 case MSG_ON_NOTIFICATION_CHANNEL_MODIFIED: {
2240 SomeArgs args = (SomeArgs) msg.obj;
2241 String pkgName = (String) args.arg1;
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002242 UserHandle user= (UserHandle) args.arg2;
2243 NotificationChannel channel = (NotificationChannel) args.arg3;
2244 int modificationType = (int) args.arg4;
2245 onNotificationChannelModified(pkgName, user, channel, modificationType);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002246 } break;
2247
2248 case MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED: {
2249 SomeArgs args = (SomeArgs) msg.obj;
2250 String pkgName = (String) args.arg1;
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002251 UserHandle user = (UserHandle) args.arg2;
2252 NotificationChannelGroup group = (NotificationChannelGroup) args.arg3;
2253 int modificationType = (int) args.arg4;
2254 onNotificationChannelGroupModified(pkgName, user, group, modificationType);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002255 } break;
Julia Reynolds12ad7ca2019-01-28 09:29:16 -05002256
2257 case MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED: {
2258 onStatusBarIconsBehaviorChanged((Boolean) msg.obj);
2259 } break;
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002260 }
2261 }
2262 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -07002263}