blob: d3285bb53eb3e2f254aff074c4fb5fc63d0e8c59 [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
428 public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
429 NotificationStats stats, int reason) {
430 onNotificationRemoved(sbn, rankingMap, reason);
431 }
432
433 /**
John Spurlocka4294292014-03-24 18:02:32 -0400434 * Implement this method to learn about when the listener is enabled and connected to
Christoph Studercee44ba2014-05-20 18:36:43 +0200435 * the notification manager. You are safe to call {@link #getActiveNotifications()}
John Spurlocka4294292014-03-24 18:02:32 -0400436 * at this time.
John Spurlocka4294292014-03-24 18:02:32 -0400437 */
Christoph Studercee44ba2014-05-20 18:36:43 +0200438 public void onListenerConnected() {
John Spurlocka4294292014-03-24 18:02:32 -0400439 // optional
440 }
441
Chris Wrenf9536642014-04-17 10:01:54 -0400442 /**
Chris Wren5717bd62016-04-06 18:15:46 -0400443 * Implement this method to learn about when the listener is disconnected from the
444 * notification manager.You will not receive any events after this call, and may only
445 * call {@link #requestRebind(ComponentName)} at this time.
446 */
447 public void onListenerDisconnected() {
448 // optional
449 }
450
451 /**
Christoph Studer05ad4822014-05-16 14:16:03 +0200452 * Implement this method to be notified when the notification ranking changes.
Christoph Studerd0694b62014-06-04 16:36:01 +0200453 *
454 * @param rankingMap The current ranking map that can be used to retrieve ranking information
455 * for active notifications.
Chris Wrenf9536642014-04-17 10:01:54 -0400456 */
Christoph Studerd0694b62014-06-04 16:36:01 +0200457 public void onNotificationRankingUpdate(RankingMap rankingMap) {
Chris Wrenf9536642014-04-17 10:01:54 -0400458 // optional
459 }
460
John Spurlock1fa865f2014-07-21 14:56:39 -0400461 /**
462 * Implement this method to be notified when the
John Spurlockd8afe3c2014-08-01 14:04:07 -0400463 * {@link #getCurrentListenerHints() Listener hints} change.
John Spurlock1fa865f2014-07-21 14:56:39 -0400464 *
John Spurlockd8afe3c2014-08-01 14:04:07 -0400465 * @param hints The current {@link #getCurrentListenerHints() listener hints}.
John Spurlock1fa865f2014-07-21 14:56:39 -0400466 */
John Spurlockd8afe3c2014-08-01 14:04:07 -0400467 public void onListenerHintsChanged(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -0400468 // optional
469 }
470
Christoph Studer85a384b2014-08-27 20:16:15 +0200471 /**
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500472 * Implement this method to be notified when the behavior of silent notifications in the status
473 * bar changes. See {@link NotificationManager#shouldHideSilentStatusBarIcons()}.
474 *
475 * @param hideSilentStatusIcons whether or not status bar icons should be hidden for silent
476 * notifications
477 */
478 public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
479 // optional
480 }
481
482 /**
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400483 * Implement this method to learn about notification channel modifications.
484 *
485 * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
486 * device} in order to receive this callback.
487 *
488 * @param pkg The package the channel belongs to.
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400489 * @param user The user on which the change was made.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400490 * @param channel The channel that has changed.
491 * @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED},
492 * {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
493 * {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
494 */
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400495 public void onNotificationChannelModified(String pkg, UserHandle user,
496 NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400497 // optional
498 }
499
500 /**
501 * Implement this method to learn about notification channel group modifications.
502 *
503 * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
504 * device} in order to receive this callback.
505 *
506 * @param pkg The package the group belongs to.
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400507 * @param user The user on which the change was made.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400508 * @param group The group that has changed.
509 * @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED},
510 * {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
511 * {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
512 */
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400513 public void onNotificationChannelGroupModified(String pkg, UserHandle user,
514 NotificationChannelGroup group, @ChannelOrGroupModificationTypes int modificationType) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400515 // optional
516 }
517
518 /**
Christoph Studer85a384b2014-08-27 20:16:15 +0200519 * Implement this method to be notified when the
520 * {@link #getCurrentInterruptionFilter() interruption filter} changed.
521 *
522 * @param interruptionFilter The current
523 * {@link #getCurrentInterruptionFilter() interruption filter}.
524 */
525 public void onInterruptionFilterChanged(int interruptionFilter) {
526 // optional
527 }
528
Chris Wren51017d02015-12-15 15:34:46 -0500529 /** @hide */
Mathew Inwood31755f92018-12-20 13:53:36 +0000530 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Chris Wren51017d02015-12-15 15:34:46 -0500531 protected final INotificationManager getNotificationInterface() {
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700532 if (mNoMan == null) {
533 mNoMan = INotificationManager.Stub.asInterface(
534 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
535 }
536 return mNoMan;
537 }
538
539 /**
540 * Inform the notification manager about dismissal of a single notification.
541 * <p>
542 * Use this if your listener has a user interface that allows the user to dismiss individual
543 * notifications, similar to the behavior of Android's status bar and notification panel.
544 * It should be called after the user dismisses a single notification using your UI;
545 * upon being informed, the notification manager will actually remove the notification
546 * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
Chris Wren5717bd62016-04-06 18:15:46 -0400547 * <p>
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700548 * <b>Note:</b> If your listener allows the user to fire a notification's
549 * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
550 * this method at that time <i>if</i> the Notification in question has the
551 * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
552 *
Chris Wren5717bd62016-04-06 18:15:46 -0400553 * <p>The service should wait for the {@link #onListenerConnected()} event
554 * before performing this operation.
555 *
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700556 * @param pkg Package of the notifying app.
557 * @param tag Tag of the notification as specified by the notifying app in
558 * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
559 * @param id ID of the notification as specified by the notifying app in
560 * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
Kenny Guya263e4e2014-03-03 18:24:03 +0000561 * <p>
562 * @deprecated Use {@link #cancelNotification(String key)}
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700563 * instead. Beginning with {@link android.os.Build.VERSION_CODES#LOLLIPOP} this method will no longer
Kenny Guya263e4e2014-03-03 18:24:03 +0000564 * cancel the notification. It will continue to cancel the notification for applications
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700565 * whose {@code targetSdkVersion} is earlier than {@link android.os.Build.VERSION_CODES#LOLLIPOP}.
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700566 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700567 @Deprecated
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400568 public final void cancelNotification(String pkg, String tag, int id) {
John Spurlockda9a3be2014-02-12 12:12:26 -0500569 if (!isBound()) return;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700570 try {
Kenny Guya263e4e2014-03-03 18:24:03 +0000571 getNotificationInterface().cancelNotificationFromListener(
572 mWrapper, pkg, tag, id);
573 } catch (android.os.RemoteException ex) {
574 Log.v(TAG, "Unable to contact notification manager", ex);
575 }
576 }
577
578 /**
579 * Inform the notification manager about dismissal of a single notification.
580 * <p>
581 * Use this if your listener has a user interface that allows the user to dismiss individual
582 * notifications, similar to the behavior of Android's status bar and notification panel.
583 * It should be called after the user dismisses a single notification using your UI;
584 * upon being informed, the notification manager will actually remove the notification
585 * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
Chris Wren5717bd62016-04-06 18:15:46 -0400586 * <p>
Kenny Guya263e4e2014-03-03 18:24:03 +0000587 * <b>Note:</b> If your listener allows the user to fire a notification's
588 * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
589 * this method at that time <i>if</i> the Notification in question has the
590 * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
591 * <p>
Chris Wren5717bd62016-04-06 18:15:46 -0400592 *
593 * <p>The service should wait for the {@link #onListenerConnected()} event
594 * before performing this operation.
595 *
Kenny Guya263e4e2014-03-03 18:24:03 +0000596 * @param key Notification to dismiss from {@link StatusBarNotification#getKey()}.
597 */
598 public final void cancelNotification(String key) {
599 if (!isBound()) return;
600 try {
601 getNotificationInterface().cancelNotificationsFromListener(mWrapper,
Daniel Sandlerf5a78382015-05-15 23:59:36 -0400602 new String[] { key });
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700603 } catch (android.os.RemoteException ex) {
604 Log.v(TAG, "Unable to contact notification manager", ex);
605 }
606 }
607
608 /**
609 * Inform the notification manager about dismissal of all notifications.
610 * <p>
611 * Use this if your listener has a user interface that allows the user to dismiss all
612 * notifications, similar to the behavior of Android's status bar and notification panel.
613 * It should be called after the user invokes the "dismiss all" function of your UI;
614 * upon being informed, the notification manager will actually remove all active notifications
615 * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks.
616 *
Chris Wren5717bd62016-04-06 18:15:46 -0400617 * <p>The service should wait for the {@link #onListenerConnected()} event
618 * before performing this operation.
619 *
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400620 * {@see #cancelNotification(String, String, int)}
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700621 */
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400622 public final void cancelAllNotifications() {
John Spurlocka4294292014-03-24 18:02:32 -0400623 cancelNotifications(null /*all*/);
624 }
625
626 /**
627 * Inform the notification manager about dismissal of specific notifications.
628 * <p>
629 * Use this if your listener has a user interface that allows the user to dismiss
630 * multiple notifications at once.
631 *
Chris Wren5717bd62016-04-06 18:15:46 -0400632 * <p>The service should wait for the {@link #onListenerConnected()} event
633 * before performing this operation.
634 *
John Spurlocka4294292014-03-24 18:02:32 -0400635 * @param keys Notifications to dismiss, or {@code null} to dismiss all.
636 *
637 * {@see #cancelNotification(String, String, int)}
638 */
639 public final void cancelNotifications(String[] keys) {
John Spurlockda9a3be2014-02-12 12:12:26 -0500640 if (!isBound()) return;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700641 try {
John Spurlocka4294292014-03-24 18:02:32 -0400642 getNotificationInterface().cancelNotificationsFromListener(mWrapper, keys);
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700643 } catch (android.os.RemoteException ex) {
644 Log.v(TAG, "Unable to contact notification manager", ex);
645 }
646 }
647
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400648 /**
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400649 * Inform the notification manager about snoozing a specific notification.
650 * <p>
651 * Use this if your listener has a user interface that allows the user to snooze a notification
Julia Reynolds79672302017-01-12 08:30:16 -0500652 * until a given {@link SnoozeCriterion}. It should be called after the user snoozes a single
653 * notification using your UI; upon being informed, the notification manager will actually
654 * remove the notification and you will get an
655 * {@link #onNotificationRemoved(StatusBarNotification)} callback. When the snoozing period
656 * expires, you will get a {@link #onNotificationPosted(StatusBarNotification, RankingMap)}
657 * callback for the notification.
658 * @param key The key of the notification to snooze
659 * @param snoozeCriterionId The{@link SnoozeCriterion#getId()} of a context to snooze the
660 * notification until.
Julia Reynolds1327d3c2017-02-17 09:26:45 -0500661 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -0400662 * @removed
Julia Reynolds79672302017-01-12 08:30:16 -0500663 */
Julia Reynolds1327d3c2017-02-17 09:26:45 -0500664 @SystemApi
Julia Reynolds79672302017-01-12 08:30:16 -0500665 public final void snoozeNotification(String key, String snoozeCriterionId) {
666 if (!isBound()) return;
667 try {
668 getNotificationInterface().snoozeNotificationUntilContextFromListener(
669 mWrapper, key, snoozeCriterionId);
670 } catch (android.os.RemoteException ex) {
671 Log.v(TAG, "Unable to contact notification manager", ex);
672 }
673 }
674
675 /**
676 * Inform the notification manager about snoozing a specific notification.
677 * <p>
678 * Use this if your listener has a user interface that allows the user to snooze a notification
Julia Reynolds50989772017-02-23 14:32:16 -0500679 * for a time. It should be called after the user snoozes a single notification using
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400680 * your UI; upon being informed, the notification manager will actually remove the notification
681 * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. When the
682 * snoozing period expires, you will get a
683 * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
684 * notification.
685 * @param key The key of the notification to snooze
Julia Reynolds50989772017-02-23 14:32:16 -0500686 * @param durationMs A duration to snooze the notification for, in milliseconds.
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400687 */
Julia Reynolds50989772017-02-23 14:32:16 -0500688 public final void snoozeNotification(String key, long durationMs) {
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400689 if (!isBound()) return;
690 try {
Julia Reynoldsb6c1f992016-11-22 09:26:46 -0500691 getNotificationInterface().snoozeNotificationUntilFromListener(
Julia Reynolds50989772017-02-23 14:32:16 -0500692 mWrapper, key, durationMs);
Julia Reynoldsb6c1f992016-11-22 09:26:46 -0500693 } catch (android.os.RemoteException ex) {
694 Log.v(TAG, "Unable to contact notification manager", ex);
695 }
696 }
697
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400698
699 /**
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700700 * Inform the notification manager that these notifications have been viewed by the
Amith Yamasanic6ecbce2015-06-23 12:58:43 -0700701 * user. This should only be called when there is sufficient confidence that the user is
702 * looking at the notifications, such as when the notifications appear on the screen due to
703 * an explicit user interaction.
Chris Wren5717bd62016-04-06 18:15:46 -0400704 *
705 * <p>The service should wait for the {@link #onListenerConnected()} event
706 * before performing this operation.
707 *
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700708 * @param keys Notifications to mark as seen.
709 */
710 public final void setNotificationsShown(String[] keys) {
711 if (!isBound()) return;
712 try {
713 getNotificationInterface().setNotificationsShownFromListener(mWrapper, keys);
714 } catch (android.os.RemoteException ex) {
715 Log.v(TAG, "Unable to contact notification manager", ex);
716 }
717 }
718
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400719
720 /**
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400721 * Updates a notification channel for a given package for a given user. This should only be used
722 * to reflect changes a user has made to the channel via the listener's user interface.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400723 *
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400724 * <p>This method will throw a security exception if you don't have access to notifications
725 * for the given user.</p>
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400726 * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
727 * device} in order to use this method.
728 *
729 * @param pkg The package the channel belongs to.
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400730 * @param user The user the channel belongs to.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400731 * @param channel the channel to update.
732 */
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400733 public final void updateNotificationChannel(@NonNull String pkg, @NonNull UserHandle user,
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400734 @NonNull NotificationChannel channel) {
735 if (!isBound()) return;
736 try {
737 getNotificationInterface().updateNotificationChannelFromPrivilegedListener(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400738 mWrapper, pkg, user, channel);
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400739 } catch (RemoteException e) {
740 Log.v(TAG, "Unable to contact notification manager", e);
741 throw e.rethrowFromSystemServer();
742 }
743 }
744
745 /**
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400746 * Returns all notification channels belonging to the given package for a given user.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400747 *
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400748 * <p>This method will throw a security exception if you don't have access to notifications
749 * for the given user.</p>
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400750 * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
Julia Reynolds48a6ed92018-10-22 12:52:03 -0400751 * device} or be the {@link NotificationAssistantService notification assistant} in order to
752 * use this method.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400753 *
754 * @param pkg The package to retrieve channels for.
755 */
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400756 public final List<NotificationChannel> getNotificationChannels(@NonNull String pkg,
757 @NonNull UserHandle user) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400758 if (!isBound()) return null;
759 try {
760
761 return getNotificationInterface().getNotificationChannelsFromPrivilegedListener(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400762 mWrapper, pkg, user).getList();
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400763 } catch (RemoteException e) {
764 Log.v(TAG, "Unable to contact notification manager", e);
765 throw e.rethrowFromSystemServer();
766 }
767 }
768
769 /**
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400770 * Returns all notification channel groups belonging to the given package for a given user.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400771 *
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400772 * <p>This method will throw a security exception if you don't have access to notifications
773 * for the given user.</p>
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400774 * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
Julia Reynolds48a6ed92018-10-22 12:52:03 -0400775 * device} or be the {@link NotificationAssistantService notification assistant} in order to
776 * use this method.
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400777 *
778 * @param pkg The package to retrieve channel groups for.
779 */
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400780 public final List<NotificationChannelGroup> getNotificationChannelGroups(@NonNull String pkg,
781 @NonNull UserHandle user) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400782 if (!isBound()) return null;
783 try {
784
785 return getNotificationInterface().getNotificationChannelGroupsFromPrivilegedListener(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -0400786 mWrapper, pkg, user).getList();
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400787 } catch (RemoteException e) {
788 Log.v(TAG, "Unable to contact notification manager", e);
789 throw e.rethrowFromSystemServer();
790 }
791 }
792
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700793 /**
Christoph Studerb82bc782014-08-20 14:29:43 +0200794 * Sets the notification trim that will be received via {@link #onNotificationPosted}.
795 *
796 * <p>
797 * Setting a trim other than {@link #TRIM_FULL} enables listeners that don't need access to the
798 * full notification features right away to reduce their memory footprint. Full notifications
799 * can be requested on-demand via {@link #getActiveNotifications(int)}.
800 *
801 * <p>
802 * Set to {@link #TRIM_FULL} initially.
803 *
Chris Wren5717bd62016-04-06 18:15:46 -0400804 * <p>The service should wait for the {@link #onListenerConnected()} event
805 * before performing this operation.
806 *
Christoph Studerb82bc782014-08-20 14:29:43 +0200807 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -0400808 * @removed
Christoph Studerb82bc782014-08-20 14:29:43 +0200809 *
810 * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}.
811 * See <code>TRIM_*</code> constants.
812 */
813 @SystemApi
814 public final void setOnNotificationPostedTrim(int trim) {
815 if (!isBound()) return;
816 try {
817 getNotificationInterface().setOnNotificationPostedTrimFromListener(mWrapper, trim);
818 } catch (RemoteException ex) {
819 Log.v(TAG, "Unable to contact notification manager", ex);
820 }
821 }
822
823 /**
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400824 * Request the list of outstanding notifications (that is, those that are visible to the
John Spurlocka4294292014-03-24 18:02:32 -0400825 * current user). Useful when you don't know what's already been posted.
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400826 *
Chris Wren5717bd62016-04-06 18:15:46 -0400827 * <p>The service should wait for the {@link #onListenerConnected()} event
828 * before performing this operation.
829 *
Chris Wrenf9536642014-04-17 10:01:54 -0400830 * @return An array of active notifications, sorted in natural order.
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400831 */
832 public StatusBarNotification[] getActiveNotifications() {
Julia Reynolds94a38b32018-04-20 13:33:36 -0400833 StatusBarNotification[] activeNotifications = getActiveNotifications(null, TRIM_FULL);
834 return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
Christoph Studerb82bc782014-08-20 14:29:43 +0200835 }
836
837 /**
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500838 * Like {@link #getActiveNotifications()}, but returns the list of currently snoozed
839 * notifications, for all users this listener has access to.
840 *
841 * <p>The service should wait for the {@link #onListenerConnected()} event
842 * before performing this operation.
843 *
Julia Reynoldsa11d0b12017-02-16 15:01:36 -0500844 * @return An array of snoozed notifications, sorted in natural order.
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500845 */
846 public final StatusBarNotification[] getSnoozedNotifications() {
847 try {
848 ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
849 .getSnoozedNotificationsFromListener(mWrapper, TRIM_FULL);
850 return cleanUpNotificationList(parceledList);
851 } catch (android.os.RemoteException ex) {
852 Log.v(TAG, "Unable to contact notification manager", ex);
853 }
854 return null;
855 }
856
857 /**
Christoph Studerb82bc782014-08-20 14:29:43 +0200858 * Request the list of outstanding notifications (that is, those that are visible to the
859 * current user). Useful when you don't know what's already been posted.
860 *
861 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -0400862 * @removed
Christoph Studerb82bc782014-08-20 14:29:43 +0200863 *
864 * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
865 * @return An array of active notifications, sorted in natural order.
866 */
867 @SystemApi
868 public StatusBarNotification[] getActiveNotifications(int trim) {
Julia Reynolds94a38b32018-04-20 13:33:36 -0400869 StatusBarNotification[] activeNotifications = getActiveNotifications(null, trim);
870 return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
Dan Sandlerea75fdd2014-08-12 12:29:19 -0400871 }
872
873 /**
874 * Request one or more notifications by key. Useful if you have been keeping track of
875 * notifications but didn't want to retain the bits, and now need to go back and extract
876 * more data out of those notifications.
877 *
Chris Wren5717bd62016-04-06 18:15:46 -0400878 * <p>The service should wait for the {@link #onListenerConnected()} event
879 * before performing this operation.
880 *
Christoph Studerb82bc782014-08-20 14:29:43 +0200881 * @param keys the keys of the notifications to request
Dan Sandlerea75fdd2014-08-12 12:29:19 -0400882 * @return An array of notifications corresponding to the requested keys, in the
883 * same order as the key list.
884 */
885 public StatusBarNotification[] getActiveNotifications(String[] keys) {
Julia Reynolds94a38b32018-04-20 13:33:36 -0400886 StatusBarNotification[] activeNotifications = getActiveNotifications(keys, TRIM_FULL);
887 return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
Christoph Studerb82bc782014-08-20 14:29:43 +0200888 }
889
890 /**
891 * Request one or more notifications by key. Useful if you have been keeping track of
892 * notifications but didn't want to retain the bits, and now need to go back and extract
893 * more data out of those notifications.
894 *
895 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -0400896 * @removed
Christoph Studerb82bc782014-08-20 14:29:43 +0200897 *
898 * @param keys the keys of the notifications to request
899 * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
900 * @return An array of notifications corresponding to the requested keys, in the
901 * same order as the key list.
902 */
903 @SystemApi
904 public StatusBarNotification[] getActiveNotifications(String[] keys, int trim) {
905 if (!isBound())
906 return null;
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400907 try {
Christoph Studerb82bc782014-08-20 14:29:43 +0200908 ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
909 .getActiveNotificationsFromListener(mWrapper, keys, trim);
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500910 return cleanUpNotificationList(parceledList);
John Spurlocka4294292014-03-24 18:02:32 -0400911 } catch (android.os.RemoteException ex) {
912 Log.v(TAG, "Unable to contact notification manager", ex);
913 }
914 return null;
915 }
916
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500917 private StatusBarNotification[] cleanUpNotificationList(
918 ParceledListSlice<StatusBarNotification> parceledList) {
Julia Reynolds94a38b32018-04-20 13:33:36 -0400919 if (parceledList == null || parceledList.getList() == null) {
920 return new StatusBarNotification[0];
921 }
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500922 List<StatusBarNotification> list = parceledList.getList();
923 ArrayList<StatusBarNotification> corruptNotifications = null;
924 int N = list.size();
925 for (int i = 0; i < N; i++) {
926 StatusBarNotification sbn = list.get(i);
927 Notification notification = sbn.getNotification();
928 try {
929 // convert icon metadata to legacy format for older clients
930 createLegacyIconExtras(notification);
931 // populate remote views for older clients.
932 maybePopulateRemoteViews(notification);
Selim Cineke7238dd2017-12-14 17:48:32 -0800933 // populate people for older clients.
934 maybePopulatePeople(notification);
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500935 } catch (IllegalArgumentException e) {
936 if (corruptNotifications == null) {
937 corruptNotifications = new ArrayList<>(N);
938 }
939 corruptNotifications.add(sbn);
940 Log.w(TAG, "get(Active/Snoozed)Notifications: can't rebuild notification from " +
941 sbn.getPackageName());
942 }
943 }
944 if (corruptNotifications != null) {
945 list.removeAll(corruptNotifications);
946 }
947 return list.toArray(new StatusBarNotification[list.size()]);
948 }
949
John Spurlocka4294292014-03-24 18:02:32 -0400950 /**
John Spurlockd8afe3c2014-08-01 14:04:07 -0400951 * Gets the set of hints representing current state.
John Spurlock1fa865f2014-07-21 14:56:39 -0400952 *
953 * <p>
John Spurlockd8afe3c2014-08-01 14:04:07 -0400954 * The current state may differ from the requested state if the hint represents state
John Spurlock1fa865f2014-07-21 14:56:39 -0400955 * shared across all listeners or a feature the notification host does not support or refuses
956 * to grant.
957 *
Chris Wren5717bd62016-04-06 18:15:46 -0400958 * <p>The service should wait for the {@link #onListenerConnected()} event
959 * before performing this operation.
960 *
Christoph Studer85a384b2014-08-27 20:16:15 +0200961 * @return Zero or more of the HINT_ constants.
John Spurlock1fa865f2014-07-21 14:56:39 -0400962 */
John Spurlockd8afe3c2014-08-01 14:04:07 -0400963 public final int getCurrentListenerHints() {
Christoph Studer85a384b2014-08-27 20:16:15 +0200964 if (!isBound()) return 0;
John Spurlock1fa865f2014-07-21 14:56:39 -0400965 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400966 return getNotificationInterface().getHintsFromListener(mWrapper);
John Spurlock1fa865f2014-07-21 14:56:39 -0400967 } catch (android.os.RemoteException ex) {
968 Log.v(TAG, "Unable to contact notification manager", ex);
Christoph Studer85a384b2014-08-27 20:16:15 +0200969 return 0;
970 }
971 }
972
973 /**
974 * Gets the current notification interruption filter active on the host.
975 *
976 * <p>
977 * The interruption filter defines which notifications are allowed to interrupt the user
978 * (e.g. via sound &amp; vibration) and is applied globally. Listeners can find out whether
979 * a specific notification matched the interruption filter via
980 * {@link Ranking#matchesInterruptionFilter()}.
981 * <p>
982 * The current filter may differ from the previously requested filter if the notification host
983 * does not support or refuses to apply the requested filter, or if another component changed
984 * the filter in the meantime.
985 * <p>
986 * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
987 *
Chris Wren5717bd62016-04-06 18:15:46 -0400988 * <p>The service should wait for the {@link #onListenerConnected()} event
989 * before performing this operation.
990 *
John Spurlock83104102015-02-12 23:25:12 -0500991 * @return One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when
992 * unavailable.
Christoph Studer85a384b2014-08-27 20:16:15 +0200993 */
994 public final int getCurrentInterruptionFilter() {
John Spurlock83104102015-02-12 23:25:12 -0500995 if (!isBound()) return INTERRUPTION_FILTER_UNKNOWN;
Christoph Studer85a384b2014-08-27 20:16:15 +0200996 try {
Chris Wren957ed702014-09-24 18:17:36 -0400997 return getNotificationInterface().getInterruptionFilterFromListener(mWrapper);
Christoph Studer85a384b2014-08-27 20:16:15 +0200998 } catch (android.os.RemoteException ex) {
999 Log.v(TAG, "Unable to contact notification manager", ex);
John Spurlock83104102015-02-12 23:25:12 -05001000 return INTERRUPTION_FILTER_UNKNOWN;
John Spurlock1fa865f2014-07-21 14:56:39 -04001001 }
1002 }
1003
1004 /**
Julia Reynolds4703bac2018-09-12 10:39:30 -04001005 * Clears listener hints set via {@link #getCurrentListenerHints()}.
1006 *
1007 * <p>The service should wait for the {@link #onListenerConnected()} event
1008 * before performing this operation.
1009 */
1010 public final void clearRequestedListenerHints() {
1011 if (!isBound()) return;
1012 try {
1013 getNotificationInterface().clearRequestedListenerHints(mWrapper);
1014 } catch (android.os.RemoteException ex) {
1015 Log.v(TAG, "Unable to contact notification manager", ex);
1016 }
1017 }
1018
1019 /**
John Spurlockd8afe3c2014-08-01 14:04:07 -04001020 * Sets the desired {@link #getCurrentListenerHints() listener hints}.
John Spurlock1fa865f2014-07-21 14:56:39 -04001021 *
1022 * <p>
Christoph Studer85a384b2014-08-27 20:16:15 +02001023 * This is merely a request, the host may or may not choose to take action depending
John Spurlock1fa865f2014-07-21 14:56:39 -04001024 * on other listener requests or other global state.
1025 * <p>
John Spurlockd8afe3c2014-08-01 14:04:07 -04001026 * Listen for updates using {@link #onListenerHintsChanged(int)}.
John Spurlock1fa865f2014-07-21 14:56:39 -04001027 *
Chris Wren5717bd62016-04-06 18:15:46 -04001028 * <p>The service should wait for the {@link #onListenerConnected()} event
1029 * before performing this operation.
1030 *
John Spurlockd8afe3c2014-08-01 14:04:07 -04001031 * @param hints One or more of the HINT_ constants.
John Spurlock1fa865f2014-07-21 14:56:39 -04001032 */
John Spurlockd8afe3c2014-08-01 14:04:07 -04001033 public final void requestListenerHints(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04001034 if (!isBound()) return;
1035 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001036 getNotificationInterface().requestHintsFromListener(mWrapper, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04001037 } catch (android.os.RemoteException ex) {
1038 Log.v(TAG, "Unable to contact notification manager", ex);
1039 }
1040 }
1041
1042 /**
Christoph Studer85a384b2014-08-27 20:16:15 +02001043 * Sets the desired {@link #getCurrentInterruptionFilter() interruption filter}.
1044 *
1045 * <p>
1046 * This is merely a request, the host may or may not choose to apply the requested
1047 * interruption filter depending on other listener requests or other global state.
1048 * <p>
1049 * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
1050 *
Chris Wren5717bd62016-04-06 18:15:46 -04001051 * <p>The service should wait for the {@link #onListenerConnected()} event
1052 * before performing this operation.
1053 *
Christoph Studer85a384b2014-08-27 20:16:15 +02001054 * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants.
1055 */
1056 public final void requestInterruptionFilter(int interruptionFilter) {
1057 if (!isBound()) return;
1058 try {
1059 getNotificationInterface()
1060 .requestInterruptionFilterFromListener(mWrapper, interruptionFilter);
1061 } catch (android.os.RemoteException ex) {
1062 Log.v(TAG, "Unable to contact notification manager", ex);
1063 }
1064 }
1065
1066 /**
Christoph Studer05ad4822014-05-16 14:16:03 +02001067 * Returns current ranking information.
John Spurlocka4294292014-03-24 18:02:32 -04001068 *
Christoph Studer05ad4822014-05-16 14:16:03 +02001069 * <p>
1070 * The returned object represents the current ranking snapshot and only
Christoph Studerd0694b62014-06-04 16:36:01 +02001071 * applies for currently active notifications.
1072 * <p>
1073 * Generally you should use the RankingMap that is passed with events such
1074 * as {@link #onNotificationPosted(StatusBarNotification, RankingMap)},
1075 * {@link #onNotificationRemoved(StatusBarNotification, RankingMap)}, and
1076 * so on. This method should only be used when needing access outside of
1077 * such events, for example to retrieve the RankingMap right after
1078 * initialization.
Christoph Studer05ad4822014-05-16 14:16:03 +02001079 *
Chris Wren5717bd62016-04-06 18:15:46 -04001080 * <p>The service should wait for the {@link #onListenerConnected()} event
1081 * before performing this operation.
1082 *
Christoph Studerd0694b62014-06-04 16:36:01 +02001083 * @return A {@link RankingMap} object providing access to ranking information
John Spurlocka4294292014-03-24 18:02:32 -04001084 */
Christoph Studerd0694b62014-06-04 16:36:01 +02001085 public RankingMap getCurrentRanking() {
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001086 synchronized (mLock) {
1087 return mRankingMap;
1088 }
Daniel Sandler25cf8ce2013-04-24 15:34:57 -04001089 }
1090
Chris Wren5717bd62016-04-06 18:15:46 -04001091 /**
1092 * This is not the lifecycle event you are looking for.
1093 *
1094 * <p>The service should wait for the {@link #onListenerConnected()} event
1095 * before performing any operations.
1096 */
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001097 @Override
1098 public IBinder onBind(Intent intent) {
1099 if (mWrapper == null) {
Chris Wren51017d02015-12-15 15:34:46 -05001100 mWrapper = new NotificationListenerWrapper();
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001101 }
1102 return mWrapper;
1103 }
1104
Chris Wren51017d02015-12-15 15:34:46 -05001105 /** @hide */
Mathew Inwoode3807372018-08-10 09:51:03 +01001106 @UnsupportedAppUsage
Chris Wren51017d02015-12-15 15:34:46 -05001107 protected boolean isBound() {
John Spurlockda9a3be2014-02-12 12:12:26 -05001108 if (mWrapper == null) {
1109 Log.w(TAG, "Notification listener service not yet bound.");
1110 return false;
1111 }
1112 return true;
1113 }
1114
Chris Wren5717bd62016-04-06 18:15:46 -04001115 @Override
1116 public void onDestroy() {
1117 onListenerDisconnected();
1118 super.onDestroy();
1119 }
1120
Chris Wren1941fc72014-05-14 15:20:51 -04001121 /**
1122 * Directly register this service with the Notification Manager.
1123 *
1124 * <p>Only system services may use this call. It will fail for non-system callers.
1125 * Apps should ask the user to add their listener in Settings.
1126 *
Christoph Studer4600f9b2014-07-22 22:44:43 +02001127 * @param context Context required for accessing resources. Since this service isn't
1128 * launched as a real Service when using this method, a context has to be passed in.
Chris Wren1941fc72014-05-14 15:20:51 -04001129 * @param componentName the component that will consume the notification information
1130 * @param currentUser the user to use as the stream filter
1131 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -04001132 * @removed
Chris Wren1941fc72014-05-14 15:20:51 -04001133 */
Jeff Brown5c507c12014-06-05 17:14:39 -07001134 @SystemApi
Christoph Studer4600f9b2014-07-22 22:44:43 +02001135 public void registerAsSystemService(Context context, ComponentName componentName,
1136 int currentUser) throws RemoteException {
Chris Wren1941fc72014-05-14 15:20:51 -04001137 if (mWrapper == null) {
Chris Wren51017d02015-12-15 15:34:46 -05001138 mWrapper = new NotificationListenerWrapper();
Chris Wren1941fc72014-05-14 15:20:51 -04001139 }
Chris Wren0efdb882016-03-01 17:17:47 -05001140 mSystemContext = context;
Chris Wren1941fc72014-05-14 15:20:51 -04001141 INotificationManager noMan = getNotificationInterface();
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001142 mHandler = new MyHandler(context.getMainLooper());
Chris Wrenb66a16d2016-04-29 10:07:48 -04001143 mCurrentUser = currentUser;
1144 noMan.registerListener(mWrapper, componentName, currentUser);
Chris Wren1941fc72014-05-14 15:20:51 -04001145 }
1146
1147 /**
1148 * Directly unregister this service from the Notification Manager.
1149 *
Chris Wren5717bd62016-04-06 18:15:46 -04001150 * <p>This method will fail for listeners that were not registered
Chris Wren1941fc72014-05-14 15:20:51 -04001151 * with (@link registerAsService).
1152 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -04001153 * @removed
Chris Wren1941fc72014-05-14 15:20:51 -04001154 */
Jeff Brown5c507c12014-06-05 17:14:39 -07001155 @SystemApi
Chris Wren1941fc72014-05-14 15:20:51 -04001156 public void unregisterAsSystemService() throws RemoteException {
1157 if (mWrapper != null) {
1158 INotificationManager noMan = getNotificationInterface();
1159 noMan.unregisterListener(mWrapper, mCurrentUser);
1160 }
1161 }
1162
Chris Wrenab41eec2016-01-04 18:01:27 -05001163 /**
Elliot Waite54de7742017-01-11 15:30:35 -08001164 * Request that the listener be rebound, after a previous call to {@link #requestUnbind}.
Chris Wrenab41eec2016-01-04 18:01:27 -05001165 *
Chris Wren5717bd62016-04-06 18:15:46 -04001166 * <p>This method will fail for listeners that have
Chris Wrenab41eec2016-01-04 18:01:27 -05001167 * not been granted the permission by the user.
Chris Wrenab41eec2016-01-04 18:01:27 -05001168 */
Chris Wrencf548bf2016-05-20 14:53:16 -04001169 public static void requestRebind(ComponentName componentName) {
Chris Wrenab41eec2016-01-04 18:01:27 -05001170 INotificationManager noMan = INotificationManager.Stub.asInterface(
1171 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
Chris Wrencf548bf2016-05-20 14:53:16 -04001172 try {
1173 noMan.requestBindListener(componentName);
1174 } catch (RemoteException ex) {
1175 throw ex.rethrowFromSystemServer();
1176 }
Chris Wrenab41eec2016-01-04 18:01:27 -05001177 }
1178
1179 /**
1180 * Request that the service be unbound.
1181 *
Julia Reynolds0ffc13b2017-04-27 13:54:36 -04001182 * <p>Once this is called, you will no longer receive updates and no method calls are
1183 * guaranteed to be successful, until you next receive the {@link #onListenerConnected()} event.
1184 * The service will likely be killed by the system after this call.
Chris Wren5717bd62016-04-06 18:15:46 -04001185 *
1186 * <p>The service should wait for the {@link #onListenerConnected()} event
1187 * before performing this operation. I know it's tempting, but you must wait.
Chris Wrenab41eec2016-01-04 18:01:27 -05001188 */
Chris Wrencf548bf2016-05-20 14:53:16 -04001189 public final void requestUnbind() {
Chris Wrenab41eec2016-01-04 18:01:27 -05001190 if (mWrapper != null) {
1191 INotificationManager noMan = getNotificationInterface();
Chris Wrencf548bf2016-05-20 14:53:16 -04001192 try {
1193 noMan.requestUnbindListener(mWrapper);
1194 // Disable future messages.
1195 isConnected = false;
1196 } catch (RemoteException ex) {
1197 throw ex.rethrowFromSystemServer();
1198 }
Chris Wrenab41eec2016-01-04 18:01:27 -05001199 }
1200 }
1201
Daniel Sandlerf5a78382015-05-15 23:59:36 -04001202 /** Convert new-style Icons to legacy representations for pre-M clients. */
1203 private void createLegacyIconExtras(Notification n) {
1204 Icon smallIcon = n.getSmallIcon();
1205 Icon largeIcon = n.getLargeIcon();
Dan Sandler99a37f12015-06-09 14:34:38 -04001206 if (smallIcon != null && smallIcon.getType() == Icon.TYPE_RESOURCE) {
Daniel Sandlerf5a78382015-05-15 23:59:36 -04001207 n.extras.putInt(Notification.EXTRA_SMALL_ICON, smallIcon.getResId());
1208 n.icon = smallIcon.getResId();
1209 }
1210 if (largeIcon != null) {
1211 Drawable d = largeIcon.loadDrawable(getContext());
1212 if (d != null && d instanceof BitmapDrawable) {
1213 final Bitmap largeIconBits = ((BitmapDrawable) d).getBitmap();
1214 n.extras.putParcelable(Notification.EXTRA_LARGE_ICON, largeIconBits);
1215 n.largeIcon = largeIconBits;
1216 }
1217 }
1218 }
1219
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001220 /**
1221 * Populates remote views for pre-N targeting apps.
1222 */
1223 private void maybePopulateRemoteViews(Notification notification) {
1224 if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
1225 Builder builder = Builder.recoverBuilder(getContext(), notification);
Adrian Roos5081c0d2016-02-26 16:04:19 -08001226
1227 // Some styles wrap Notification's contentView, bigContentView and headsUpContentView.
1228 // First inflate them all, only then set them to avoid recursive wrapping.
1229 RemoteViews content = builder.createContentView();
1230 RemoteViews big = builder.createBigContentView();
1231 RemoteViews headsUp = builder.createHeadsUpContentView();
1232
1233 notification.contentView = content;
1234 notification.bigContentView = big;
1235 notification.headsUpContentView = headsUp;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001236 }
1237 }
1238
Selim Cineke7238dd2017-12-14 17:48:32 -08001239 /**
1240 * Populates remote views for pre-P targeting apps.
1241 */
1242 private void maybePopulatePeople(Notification notification) {
1243 if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P) {
Selim Cinek9acd6732018-03-23 16:39:02 -07001244 ArrayList<Person> people = notification.extras.getParcelableArrayList(
Selim Cineke7238dd2017-12-14 17:48:32 -08001245 Notification.EXTRA_PEOPLE_LIST);
1246 if (people != null && people.isEmpty()) {
1247 int size = people.size();
1248 String[] peopleArray = new String[size];
1249 for (int i = 0; i < size; i++) {
Selim Cinek9acd6732018-03-23 16:39:02 -07001250 Person person = people.get(i);
Selim Cineke7238dd2017-12-14 17:48:32 -08001251 peopleArray[i] = person.resolveToLegacyUri();
1252 }
1253 notification.extras.putStringArray(Notification.EXTRA_PEOPLE, peopleArray);
1254 }
1255 }
1256 }
1257
Chris Wren51017d02015-12-15 15:34:46 -05001258 /** @hide */
1259 protected class NotificationListenerWrapper extends INotificationListener.Stub {
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001260 @Override
Griff Hazen84a00ea2014-09-02 17:10:47 -07001261 public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
Christoph Studer05ad4822014-05-16 14:16:03 +02001262 NotificationRankingUpdate update) {
Griff Hazen84a00ea2014-09-02 17:10:47 -07001263 StatusBarNotification sbn;
1264 try {
1265 sbn = sbnHolder.get();
1266 } catch (RemoteException e) {
1267 Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);
1268 return;
1269 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02001270
Chris Wren24fb8942015-06-18 14:33:56 -04001271 try {
Chris Wren24fb8942015-06-18 14:33:56 -04001272 // convert icon metadata to legacy format for older clients
1273 createLegacyIconExtras(sbn.getNotification());
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001274 maybePopulateRemoteViews(sbn.getNotification());
Selim Cinek5390e7d2018-02-20 19:12:41 -08001275 maybePopulatePeople(sbn.getNotification());
Chris Wren24fb8942015-06-18 14:33:56 -04001276 } catch (IllegalArgumentException e) {
Andreas Gampe1ed71f32015-12-11 15:49:07 -08001277 // warn and drop corrupt notification
Chris Wren24fb8942015-06-18 14:33:56 -04001278 Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +
1279 sbn.getPackageName());
Andreas Gampe1ed71f32015-12-11 15:49:07 -08001280 sbn = null;
Chris Wren24fb8942015-06-18 14:33:56 -04001281 }
Daniel Sandlerf5a78382015-05-15 23:59:36 -04001282
Christoph Studer05ad4822014-05-16 14:16:03 +02001283 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001284 synchronized (mLock) {
1285 applyUpdateLocked(update);
1286 if (sbn != null) {
1287 SomeArgs args = SomeArgs.obtain();
1288 args.arg1 = sbn;
1289 args.arg2 = mRankingMap;
1290 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,
1291 args).sendToTarget();
1292 } else {
1293 // still pass along the ranking map, it may contain other information
1294 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
1295 mRankingMap).sendToTarget();
Chris Wrenf9536642014-04-17 10:01:54 -04001296 }
John Spurlockc133ab82013-06-10 15:16:22 -04001297 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001298
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001299 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001300
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001301 @Override
Griff Hazen84a00ea2014-09-02 17:10:47 -07001302 public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,
Julia Reynolds503ed942017-10-04 16:04:56 -04001303 NotificationRankingUpdate update, NotificationStats stats, int reason) {
Griff Hazen84a00ea2014-09-02 17:10:47 -07001304 StatusBarNotification sbn;
1305 try {
1306 sbn = sbnHolder.get();
1307 } catch (RemoteException e) {
1308 Log.w(TAG, "onNotificationRemoved: Error receiving StatusBarNotification", e);
1309 return;
1310 }
Christoph Studer05ad4822014-05-16 14:16:03 +02001311 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001312 synchronized (mLock) {
1313 applyUpdateLocked(update);
1314 SomeArgs args = SomeArgs.obtain();
1315 args.arg1 = sbn;
1316 args.arg2 = mRankingMap;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05001317 args.arg3 = reason;
Julia Reynolds503ed942017-10-04 16:04:56 -04001318 args.arg4 = stats;
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001319 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED,
1320 args).sendToTarget();
John Spurlockc133ab82013-06-10 15:16:22 -04001321 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001322
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001323 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001324
John Spurlocka4294292014-03-24 18:02:32 -04001325 @Override
Christoph Studer05ad4822014-05-16 14:16:03 +02001326 public void onListenerConnected(NotificationRankingUpdate update) {
1327 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001328 synchronized (mLock) {
1329 applyUpdateLocked(update);
John Spurlocka4294292014-03-24 18:02:32 -04001330 }
Chris Wren5717bd62016-04-06 18:15:46 -04001331 isConnected = true;
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001332 mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_CONNECTED).sendToTarget();
John Spurlocka4294292014-03-24 18:02:32 -04001333 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001334
Chris Wrenf9536642014-04-17 10:01:54 -04001335 @Override
Christoph Studer05ad4822014-05-16 14:16:03 +02001336 public void onNotificationRankingUpdate(NotificationRankingUpdate update)
Chris Wrenf9536642014-04-17 10:01:54 -04001337 throws RemoteException {
Christoph Studer05ad4822014-05-16 14:16:03 +02001338 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001339 synchronized (mLock) {
1340 applyUpdateLocked(update);
1341 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
1342 mRankingMap).sendToTarget();
Chris Wrenf9536642014-04-17 10:01:54 -04001343 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001344
Chris Wrenf9536642014-04-17 10:01:54 -04001345 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001346
John Spurlock1fa865f2014-07-21 14:56:39 -04001347 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001348 public void onListenerHintsChanged(int hints) throws RemoteException {
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001349 mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_HINTS_CHANGED,
1350 hints, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -04001351 }
Christoph Studer85a384b2014-08-27 20:16:15 +02001352
1353 @Override
1354 public void onInterruptionFilterChanged(int interruptionFilter) throws RemoteException {
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001355 mHandler.obtainMessage(MyHandler.MSG_ON_INTERRUPTION_FILTER_CHANGED,
1356 interruptionFilter, 0).sendToTarget();
Christoph Studer85a384b2014-08-27 20:16:15 +02001357 }
Chris Wren51017d02015-12-15 15:34:46 -05001358
1359 @Override
Julia Reynolds901bf282018-08-14 10:09:36 -04001360 public void onNotificationEnqueuedWithChannel(
1361 IStatusBarNotificationHolder notificationHolder, NotificationChannel channel)
Julia Reynoldsceecfcf2017-01-31 09:44:26 -05001362 throws RemoteException {
Chris Wren51017d02015-12-15 15:34:46 -05001363 // no-op in the listener
1364 }
1365
1366 @Override
Julia Reynolds6a63d1b2018-08-14 16:59:33 -04001367 public void onNotificationsSeen(List<String> keys)
1368 throws RemoteException {
1369 // no-op in the listener
1370 }
1371
1372 @Override
Julia Reynolds79672302017-01-12 08:30:16 -05001373 public void onNotificationSnoozedUntilContext(
1374 IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId)
Chris Wren51017d02015-12-15 15:34:46 -05001375 throws RemoteException {
1376 // no-op in the listener
1377 }
1378
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001379 @Override
Tony Makeda84a72018-11-19 17:01:32 +00001380 public void onNotificationExpansionChanged(
1381 String key, boolean isUserAction, boolean isExpanded) {
1382 // no-op in the listener
1383 }
1384
1385 @Override
1386 public void onNotificationDirectReply(String key) {
1387 // no-op in the listener
1388 }
1389
1390 @Override
Tony Mak29996702018-11-26 16:23:34 +00001391 public void onSuggestedReplySent(String key, CharSequence reply, int source) {
1392 // no-op in the listener
1393 }
1394
1395 @Override
Tony Mak7d4b3a52018-11-27 17:29:36 +00001396 public void onActionClicked(String key, Notification.Action action, int source) {
1397 // no-op in the listener
1398 }
1399
1400 @Override
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001401 public void onNotificationChannelModification(String pkgName, UserHandle user,
1402 NotificationChannel channel,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001403 @ChannelOrGroupModificationTypes int modificationType) {
1404 SomeArgs args = SomeArgs.obtain();
1405 args.arg1 = pkgName;
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001406 args.arg2 = user;
1407 args.arg3 = channel;
1408 args.arg4 = modificationType;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001409 mHandler.obtainMessage(
1410 MyHandler.MSG_ON_NOTIFICATION_CHANNEL_MODIFIED, args).sendToTarget();
1411 }
1412
1413 @Override
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001414 public void onNotificationChannelGroupModification(String pkgName, UserHandle user,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001415 NotificationChannelGroup group,
1416 @ChannelOrGroupModificationTypes int modificationType) {
1417 SomeArgs args = SomeArgs.obtain();
1418 args.arg1 = pkgName;
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001419 args.arg2 = user;
1420 args.arg3 = group;
1421 args.arg4 = modificationType;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001422 mHandler.obtainMessage(
1423 MyHandler.MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED, args).sendToTarget();
1424 }
Julia Reynolds12ad7ca2019-01-28 09:29:16 -05001425
1426 @Override
1427 public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
1428 mHandler.obtainMessage(MyHandler.MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED,
1429 hideSilentStatusIcons).sendToTarget();
1430 }
Chris Wrenf9536642014-04-17 10:01:54 -04001431 }
1432
Julia Reynolds22f02b32016-12-01 15:05:13 -05001433 /**
1434 * @hide
1435 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08001436 @GuardedBy("mLock")
Julia Reynolds22f02b32016-12-01 15:05:13 -05001437 public final void applyUpdateLocked(NotificationRankingUpdate update) {
Christoph Studerd0694b62014-06-04 16:36:01 +02001438 mRankingMap = new RankingMap(update);
1439 }
1440
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001441 /** @hide */
1442 protected Context getContext() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02001443 if (mSystemContext != null) {
1444 return mSystemContext;
1445 }
1446 return this;
1447 }
1448
Christoph Studerd0694b62014-06-04 16:36:01 +02001449 /**
Christoph Studer1d599da2014-06-12 15:25:59 +02001450 * Stores ranking related information on a currently active notification.
Christoph Studerd0694b62014-06-04 16:36:01 +02001451 *
1452 * <p>
Christoph Studer1d599da2014-06-12 15:25:59 +02001453 * Ranking objects aren't automatically updated as notification events
1454 * occur. Instead, ranking information has to be retrieved again via the
1455 * current {@link RankingMap}.
Christoph Studerd0694b62014-06-04 16:36:01 +02001456 */
1457 public static class Ranking {
Julia Reynolds0edb50c2016-02-26 14:08:25 -05001458
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001459 /** Value signifying that the user has not expressed a per-app visibility override value.
1460 * @hide */
Chris Wren5ab5c742016-05-10 15:32:23 -04001461 public static final int VISIBILITY_NO_OVERRIDE = NotificationManager.VISIBILITY_NO_OVERRIDE;
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001462
Julia Reynolds503ed942017-10-04 16:04:56 -04001463 /**
1464 * The user is likely to have a negative reaction to this notification.
1465 */
1466 public static final int USER_SENTIMENT_NEGATIVE = -1;
1467 /**
1468 * It is not known how the user will react to this notification.
1469 */
1470 public static final int USER_SENTIMENT_NEUTRAL = 0;
1471 /**
1472 * The user is likely to have a positive reaction to this notification.
1473 */
1474 public static final int USER_SENTIMENT_POSITIVE = 1;
1475
1476 /** @hide */
1477 @IntDef(prefix = { "USER_SENTIMENT_" }, value = {
1478 USER_SENTIMENT_NEGATIVE, USER_SENTIMENT_NEUTRAL, USER_SENTIMENT_POSITIVE
1479 })
1480 @Retention(RetentionPolicy.SOURCE)
1481 public @interface UserSentiment {}
1482
Christoph Studer1d599da2014-06-12 15:25:59 +02001483 private String mKey;
1484 private int mRank = -1;
1485 private boolean mIsAmbient;
Christoph Studerce7d6d22014-08-26 19:21:31 +02001486 private boolean mMatchesInterruptionFilter;
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001487 private int mVisibilityOverride;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001488 private int mSuppressedVisualEffects;
Chris Wren5ab5c742016-05-10 15:32:23 -04001489 private @NotificationManager.Importance int mImportance;
Chris Wrenbdf33762015-12-04 15:50:51 -05001490 private CharSequence mImportanceExplanation;
Julia Reynoldse46bb372016-03-17 11:05:58 -04001491 // System specified group key.
1492 private String mOverrideGroupKey;
Julia Reynolds22f02b32016-12-01 15:05:13 -05001493 // Notification assistant channel override.
Julia Reynolds924eed12017-01-19 09:52:07 -05001494 private NotificationChannel mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -05001495 // Notification assistant people override.
1496 private ArrayList<String> mOverridePeople;
1497 // Notification assistant snooze criteria.
1498 private ArrayList<SnoozeCriterion> mSnoozeCriteria;
Julia Reynolds924eed12017-01-19 09:52:07 -05001499 private boolean mShowBadge;
Julia Reynolds503ed942017-10-04 16:04:56 -04001500 private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
Beverly5a20a5e2018-03-06 15:02:44 -05001501 private boolean mHidden;
Gus Prevas7306b902018-12-11 10:57:06 -05001502 private long mLastAudiblyAlertedMs;
Gus Prevas9abc5062018-10-31 16:11:04 -04001503 private boolean mNoisy;
Tony Mak628cb932018-06-19 18:30:41 +01001504 private ArrayList<Notification.Action> mSmartActions;
Tony Makc9acf672018-07-20 13:58:24 +02001505 private ArrayList<CharSequence> mSmartReplies;
Julia Reynolds4509ce72019-01-31 13:12:43 -05001506 private boolean mCanBubble;
Christoph Studerd0694b62014-06-04 16:36:01 +02001507
Christoph Studer1d599da2014-06-12 15:25:59 +02001508 public Ranking() {}
Christoph Studerd0694b62014-06-04 16:36:01 +02001509
1510 /**
1511 * Returns the key of the notification this Ranking applies to.
1512 */
1513 public String getKey() {
1514 return mKey;
1515 }
1516
1517 /**
1518 * Returns the rank of the notification.
1519 *
1520 * @return the rank of the notification, that is the 0-based index in
1521 * the list of active notifications.
1522 */
1523 public int getRank() {
1524 return mRank;
1525 }
1526
1527 /**
1528 * Returns whether the notification is an ambient notification, that is
1529 * a notification that doesn't require the user's immediate attention.
1530 */
1531 public boolean isAmbient() {
1532 return mIsAmbient;
1533 }
1534
1535 /**
Julia Reynolds924eed12017-01-19 09:52:07 -05001536 * Returns the user specified visibility for the package that posted
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001537 * this notification, or
1538 * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if
1539 * no such preference has been expressed.
1540 * @hide
1541 */
Mathew Inwoode3807372018-08-10 09:51:03 +01001542 @UnsupportedAppUsage
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001543 public int getVisibilityOverride() {
1544 return mVisibilityOverride;
1545 }
1546
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001547 /**
1548 * Returns the type(s) of visual effects that should be suppressed for this notification.
Julia Reynoldsccc6ae62018-03-01 16:24:49 -05001549 * See {@link NotificationManager.Policy}, e.g.
1550 * {@link NotificationManager.Policy#SUPPRESSED_EFFECT_LIGHTS}.
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001551 */
1552 public int getSuppressedVisualEffects() {
1553 return mSuppressedVisualEffects;
1554 }
1555
Christoph Studerce7d6d22014-08-26 19:21:31 +02001556 /**
1557 * Returns whether the notification matches the user's interruption
1558 * filter.
Chris Wren0fef44d2014-09-30 13:05:14 -04001559 *
1560 * @return {@code true} if the notification is allowed by the filter, or
1561 * {@code false} if it is blocked.
Christoph Studerce7d6d22014-08-26 19:21:31 +02001562 */
1563 public boolean matchesInterruptionFilter() {
1564 return mMatchesInterruptionFilter;
Christoph Studer1d599da2014-06-12 15:25:59 +02001565 }
1566
Chris Wren9fa689f2015-11-20 16:44:53 -05001567 /**
1568 * Returns the importance of the notification, which dictates its
Chris Wren5ab5c742016-05-10 15:32:23 -04001569 * modes of presentation, see: {@link NotificationManager#IMPORTANCE_DEFAULT}, etc.
Chris Wren9fa689f2015-11-20 16:44:53 -05001570 *
Julia Reynolds924eed12017-01-19 09:52:07 -05001571 * @return the importance of the notification
Chris Wren9fa689f2015-11-20 16:44:53 -05001572 */
Chris Wren5ab5c742016-05-10 15:32:23 -04001573 public @NotificationManager.Importance int getImportance() {
Chris Wrenbdf33762015-12-04 15:50:51 -05001574 return mImportance;
Chris Wren9fa689f2015-11-20 16:44:53 -05001575 }
1576
1577 /**
Julia Reynolds22f02b32016-12-01 15:05:13 -05001578 * If the importance has been overridden by user preference, then this will be non-null,
Chris Wren9fa689f2015-11-20 16:44:53 -05001579 * and should be displayed to the user.
1580 *
1581 * @return the explanation for the importance, or null if it is the natural importance
1582 */
1583 public CharSequence getImportanceExplanation() {
Chris Wrenbdf33762015-12-04 15:50:51 -05001584 return mImportanceExplanation;
Chris Wren9fa689f2015-11-20 16:44:53 -05001585 }
1586
Julia Reynoldse46bb372016-03-17 11:05:58 -04001587 /**
Julia Reynolds22f02b32016-12-01 15:05:13 -05001588 * If the system has overridden the group key, then this will be non-null, and this
Julia Reynoldse46bb372016-03-17 11:05:58 -04001589 * key should be used to bundle notifications.
1590 */
1591 public String getOverrideGroupKey() {
1592 return mOverrideGroupKey;
1593 }
1594
Julia Reynolds22f02b32016-12-01 15:05:13 -05001595 /**
Julia Reynolds924eed12017-01-19 09:52:07 -05001596 * Returns the notification channel this notification was posted to, which dictates
1597 * notification behavior and presentation.
Julia Reynolds22f02b32016-12-01 15:05:13 -05001598 */
1599 public NotificationChannel getChannel() {
Julia Reynolds924eed12017-01-19 09:52:07 -05001600 return mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -05001601 }
1602
1603 /**
Julia Reynolds503ed942017-10-04 16:04:56 -04001604 * Returns how the system thinks the user feels about notifications from the
1605 * channel provided by {@link #getChannel()}. You can use this information to expose
1606 * controls to help the user block this channel's notifications, if the sentiment is
1607 * {@link #USER_SENTIMENT_NEGATIVE}, or emphasize this notification if the sentiment is
1608 * {@link #USER_SENTIMENT_POSITIVE}.
1609 */
1610 public int getUserSentiment() {
1611 return mUserSentiment;
1612 }
1613
1614 /**
Julia Reynolds22f02b32016-12-01 15:05:13 -05001615 * If the {@link NotificationAssistantService} has added people to this notification, then
1616 * this will be non-null.
Julia Reynolds1327d3c2017-02-17 09:26:45 -05001617 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -04001618 * @removed
Julia Reynolds22f02b32016-12-01 15:05:13 -05001619 */
Julia Reynolds1327d3c2017-02-17 09:26:45 -05001620 @SystemApi
Julia Reynolds22f02b32016-12-01 15:05:13 -05001621 public List<String> getAdditionalPeople() {
1622 return mOverridePeople;
1623 }
1624
1625 /**
1626 * Returns snooze criteria provided by the {@link NotificationAssistantService}. If your
1627 * user interface displays options for snoozing notifications these criteria should be
1628 * displayed as well.
Julia Reynolds1327d3c2017-02-17 09:26:45 -05001629 * @hide
Julia Reynolds7ca33072017-06-29 13:58:24 -04001630 * @removed
Julia Reynolds22f02b32016-12-01 15:05:13 -05001631 */
Julia Reynolds1327d3c2017-02-17 09:26:45 -05001632 @SystemApi
Julia Reynolds22f02b32016-12-01 15:05:13 -05001633 public List<SnoozeCriterion> getSnoozeCriteria() {
1634 return mSnoozeCriteria;
1635 }
1636
Julia Reynolds924eed12017-01-19 09:52:07 -05001637 /**
Fabian Kozynskib0da4bc2019-01-15 17:44:27 -05001638 * Returns a list of smart {@link Notification.Action} that can be added by the
1639 * {@link NotificationAssistantService}
Tony Mak628cb932018-06-19 18:30:41 +01001640 */
1641 public List<Notification.Action> getSmartActions() {
1642 return mSmartActions;
1643 }
1644
1645 /**
Fabian Kozynskib0da4bc2019-01-15 17:44:27 -05001646 * Returns a list of smart replies that can be added by the
1647 * {@link NotificationAssistantService}
Tony Makc9acf672018-07-20 13:58:24 +02001648 */
1649 public List<CharSequence> getSmartReplies() {
1650 return mSmartReplies;
1651 }
1652
1653 /**
Julia Reynolds924eed12017-01-19 09:52:07 -05001654 * Returns whether this notification can be displayed as a badge.
1655 *
1656 * @return true if the notification can be displayed as a badge, false otherwise.
1657 */
1658 public boolean canShowBadge() {
1659 return mShowBadge;
1660 }
1661
Dan Sandler1d958f82018-01-09 21:10:26 -05001662 /**
Beverly5a20a5e2018-03-06 15:02:44 -05001663 * Returns whether the app that posted this notification is suspended, so this notification
1664 * should be hidden.
1665 *
1666 * @return true if the notification should be hidden, false otherwise.
1667 */
1668 public boolean isSuspended() {
1669 return mHidden;
1670 }
1671
1672 /**
Gus Prevas7306b902018-12-11 10:57:06 -05001673 * Returns the last time this notification alerted the user via sound or vibration.
Gus Prevasa3226492018-10-23 11:10:09 -04001674 *
Gus Prevas7306b902018-12-11 10:57:06 -05001675 * @return the time of the last alerting behavior, in milliseconds.
Gus Prevasa3226492018-10-23 11:10:09 -04001676 */
Gus Prevas7306b902018-12-11 10:57:06 -05001677 public long getLastAudiblyAlertedMillis() {
1678 return mLastAudiblyAlertedMs;
Gus Prevasa3226492018-10-23 11:10:09 -04001679 }
1680
Julia Reynolds4509ce72019-01-31 13:12:43 -05001681 /**
1682 * Returns whether the user has allowed bubbles globally, at the app level, and at the
1683 * channel level for this notification.
1684 *
1685 * <p>This does not take into account the current importance of the notification, the
1686 * current DND state, or whether the posting app is foreground.</p>
1687 */
1688 public boolean canBubble() {
1689 return mCanBubble;
1690 }
1691
Gus Prevas9abc5062018-10-31 16:11:04 -04001692 /** @hide */
1693 public boolean isNoisy() {
1694 return mNoisy;
1695 }
1696
Gus Prevasa3226492018-10-23 11:10:09 -04001697 /**
Dan Sandler1d958f82018-01-09 21:10:26 -05001698 * @hide
1699 */
1700 @VisibleForTesting
1701 public void populate(String key, int rank, boolean matchesInterruptionFilter,
Julia Reynolds0421e6d2016-01-08 09:51:24 -05001702 int visibilityOverride, int suppressedVisualEffects, int importance,
Julia Reynolds22f02b32016-12-01 15:05:13 -05001703 CharSequence explanation, String overrideGroupKey,
Julia Reynolds924eed12017-01-19 09:52:07 -05001704 NotificationChannel channel, ArrayList<String> overridePeople,
Julia Reynolds503ed942017-10-04 16:04:56 -04001705 ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
Gus Prevas7306b902018-12-11 10:57:06 -05001706 int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
Gus Prevas9abc5062018-10-31 16:11:04 -04001707 boolean noisy, ArrayList<Notification.Action> smartActions,
Julia Reynolds4509ce72019-01-31 13:12:43 -05001708 ArrayList<CharSequence> smartReplies, boolean canBubble) {
Christoph Studer1d599da2014-06-12 15:25:59 +02001709 mKey = key;
1710 mRank = rank;
Julia Reynolds85769912016-10-25 09:08:57 -04001711 mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
Christoph Studerce7d6d22014-08-26 19:21:31 +02001712 mMatchesInterruptionFilter = matchesInterruptionFilter;
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001713 mVisibilityOverride = visibilityOverride;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001714 mSuppressedVisualEffects = suppressedVisualEffects;
Chris Wrenbdf33762015-12-04 15:50:51 -05001715 mImportance = importance;
1716 mImportanceExplanation = explanation;
Julia Reynoldse46bb372016-03-17 11:05:58 -04001717 mOverrideGroupKey = overrideGroupKey;
Julia Reynolds924eed12017-01-19 09:52:07 -05001718 mChannel = channel;
Julia Reynolds22f02b32016-12-01 15:05:13 -05001719 mOverridePeople = overridePeople;
1720 mSnoozeCriteria = snoozeCriteria;
Julia Reynolds924eed12017-01-19 09:52:07 -05001721 mShowBadge = showBadge;
Julia Reynolds503ed942017-10-04 16:04:56 -04001722 mUserSentiment = userSentiment;
Beverly5a20a5e2018-03-06 15:02:44 -05001723 mHidden = hidden;
Gus Prevas7306b902018-12-11 10:57:06 -05001724 mLastAudiblyAlertedMs = lastAudiblyAlertedMs;
Gus Prevas9abc5062018-10-31 16:11:04 -04001725 mNoisy = noisy;
Tony Mak628cb932018-06-19 18:30:41 +01001726 mSmartActions = smartActions;
Tony Makc9acf672018-07-20 13:58:24 +02001727 mSmartReplies = smartReplies;
Julia Reynolds4509ce72019-01-31 13:12:43 -05001728 mCanBubble = canBubble;
Christoph Studerd0694b62014-06-04 16:36:01 +02001729 }
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001730
1731 /**
1732 * {@hide}
1733 */
1734 public static String importanceToString(int importance) {
1735 switch (importance) {
Julia Reynolds85769912016-10-25 09:08:57 -04001736 case NotificationManager.IMPORTANCE_UNSPECIFIED:
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001737 return "UNSPECIFIED";
Julia Reynolds85769912016-10-25 09:08:57 -04001738 case NotificationManager.IMPORTANCE_NONE:
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001739 return "NONE";
Julia Reynolds85769912016-10-25 09:08:57 -04001740 case NotificationManager.IMPORTANCE_MIN:
Julia Reynoldsf0f629f2016-02-25 09:34:04 -05001741 return "MIN";
Julia Reynolds85769912016-10-25 09:08:57 -04001742 case NotificationManager.IMPORTANCE_LOW:
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001743 return "LOW";
Julia Reynolds85769912016-10-25 09:08:57 -04001744 case NotificationManager.IMPORTANCE_DEFAULT:
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001745 return "DEFAULT";
Julia Reynolds85769912016-10-25 09:08:57 -04001746 case NotificationManager.IMPORTANCE_HIGH:
1747 case NotificationManager.IMPORTANCE_MAX:
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001748 return "HIGH";
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001749 default:
1750 return "UNKNOWN(" + String.valueOf(importance) + ")";
1751 }
1752 }
Christoph Studer05ad4822014-05-16 14:16:03 +02001753 }
1754
1755 /**
1756 * Provides access to ranking information on currently active
1757 * notifications.
1758 *
1759 * <p>
1760 * Note that this object represents a ranking snapshot that only applies to
1761 * notifications active at the time of retrieval.
1762 */
Christoph Studerd0694b62014-06-04 16:36:01 +02001763 public static class RankingMap implements Parcelable {
Christoph Studer05ad4822014-05-16 14:16:03 +02001764 private final NotificationRankingUpdate mRankingUpdate;
Christoph Studerdda48f12014-07-29 23:13:16 +02001765 private ArrayMap<String,Integer> mRanks;
1766 private ArraySet<Object> mIntercepted;
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001767 private ArrayMap<String, Integer> mVisibilityOverrides;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001768 private ArrayMap<String, Integer> mSuppressedVisualEffects;
Chris Wrenbdf33762015-12-04 15:50:51 -05001769 private ArrayMap<String, Integer> mImportance;
1770 private ArrayMap<String, String> mImportanceExplanation;
Julia Reynoldse46bb372016-03-17 11:05:58 -04001771 private ArrayMap<String, String> mOverrideGroupKeys;
Julia Reynolds924eed12017-01-19 09:52:07 -05001772 private ArrayMap<String, NotificationChannel> mChannels;
Julia Reynolds22f02b32016-12-01 15:05:13 -05001773 private ArrayMap<String, ArrayList<String>> mOverridePeople;
1774 private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
Julia Reynolds924eed12017-01-19 09:52:07 -05001775 private ArrayMap<String, Boolean> mShowBadge;
Julia Reynolds503ed942017-10-04 16:04:56 -04001776 private ArrayMap<String, Integer> mUserSentiment;
Beverly5a20a5e2018-03-06 15:02:44 -05001777 private ArrayMap<String, Boolean> mHidden;
Gus Prevas7306b902018-12-11 10:57:06 -05001778 private ArrayMap<String, Long> mLastAudiblyAlerted;
Gus Prevas9abc5062018-10-31 16:11:04 -04001779 private ArrayMap<String, Boolean> mNoisy;
Tony Mak628cb932018-06-19 18:30:41 +01001780 private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions;
Tony Makc9acf672018-07-20 13:58:24 +02001781 private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies;
Julia Reynolds4509ce72019-01-31 13:12:43 -05001782 private boolean[] mCanBubble;
Christoph Studer05ad4822014-05-16 14:16:03 +02001783
Christoph Studerd0694b62014-06-04 16:36:01 +02001784 private RankingMap(NotificationRankingUpdate rankingUpdate) {
Christoph Studer05ad4822014-05-16 14:16:03 +02001785 mRankingUpdate = rankingUpdate;
1786 }
1787
1788 /**
1789 * Request the list of notification keys in their current ranking
1790 * order.
1791 *
1792 * @return An array of active notification keys, in their ranking order.
1793 */
1794 public String[] getOrderedKeys() {
1795 return mRankingUpdate.getOrderedKeys();
1796 }
1797
1798 /**
Christoph Studer1d599da2014-06-12 15:25:59 +02001799 * Populates outRanking with ranking information for the notification
1800 * with the given key.
Christoph Studer05ad4822014-05-16 14:16:03 +02001801 *
Christoph Studer1d599da2014-06-12 15:25:59 +02001802 * @return true if a valid key has been passed and outRanking has
1803 * been populated; false otherwise
Christoph Studer05ad4822014-05-16 14:16:03 +02001804 */
Christoph Studer1d599da2014-06-12 15:25:59 +02001805 public boolean getRanking(String key, Ranking outRanking) {
1806 int rank = getRank(key);
Julia Reynolds0421e6d2016-01-08 09:51:24 -05001807 outRanking.populate(key, rank, !isIntercepted(key),
Chris Wrenbdf33762015-12-04 15:50:51 -05001808 getVisibilityOverride(key), getSuppressedVisualEffects(key),
Julia Reynolds22f02b32016-12-01 15:05:13 -05001809 getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
Julia Reynolds924eed12017-01-19 09:52:07 -05001810 getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
Gus Prevasa3226492018-10-23 11:10:09 -04001811 getShowBadge(key), getUserSentiment(key), getHidden(key),
Gus Prevas7306b902018-12-11 10:57:06 -05001812 getLastAudiblyAlerted(key), getNoisy(key), getSmartActions(key),
Julia Reynolds4509ce72019-01-31 13:12:43 -05001813 getSmartReplies(key), canBubble(key));
Christoph Studer1d599da2014-06-12 15:25:59 +02001814 return rank >= 0;
Christoph Studerd0694b62014-06-04 16:36:01 +02001815 }
1816
Christoph Studer1d599da2014-06-12 15:25:59 +02001817 private int getRank(String key) {
Christoph Studerdda48f12014-07-29 23:13:16 +02001818 synchronized (this) {
1819 if (mRanks == null) {
1820 buildRanksLocked();
Christoph Studer05ad4822014-05-16 14:16:03 +02001821 }
1822 }
Christoph Studerdda48f12014-07-29 23:13:16 +02001823 Integer rank = mRanks.get(key);
1824 return rank != null ? rank : -1;
Christoph Studer1d599da2014-06-12 15:25:59 +02001825 }
1826
Christoph Studer1d599da2014-06-12 15:25:59 +02001827 private boolean isIntercepted(String key) {
Christoph Studerdda48f12014-07-29 23:13:16 +02001828 synchronized (this) {
1829 if (mIntercepted == null) {
1830 buildInterceptedSetLocked();
Christoph Studer1d599da2014-06-12 15:25:59 +02001831 }
1832 }
Christoph Studerdda48f12014-07-29 23:13:16 +02001833 return mIntercepted.contains(key);
1834 }
1835
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001836 private int getVisibilityOverride(String key) {
1837 synchronized (this) {
1838 if (mVisibilityOverrides == null) {
1839 buildVisibilityOverridesLocked();
1840 }
1841 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001842 Integer override = mVisibilityOverrides.get(key);
1843 if (override == null) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001844 return Ranking.VISIBILITY_NO_OVERRIDE;
1845 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001846 return override.intValue();
1847 }
1848
1849 private int getSuppressedVisualEffects(String key) {
1850 synchronized (this) {
1851 if (mSuppressedVisualEffects == null) {
1852 buildSuppressedVisualEffectsLocked();
1853 }
1854 }
1855 Integer suppressed = mSuppressedVisualEffects.get(key);
1856 if (suppressed == null) {
1857 return 0;
1858 }
1859 return suppressed.intValue();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001860 }
1861
Chris Wrenbdf33762015-12-04 15:50:51 -05001862 private int getImportance(String key) {
1863 synchronized (this) {
1864 if (mImportance == null) {
1865 buildImportanceLocked();
1866 }
1867 }
1868 Integer importance = mImportance.get(key);
1869 if (importance == null) {
Julia Reynolds85769912016-10-25 09:08:57 -04001870 return NotificationManager.IMPORTANCE_DEFAULT;
Chris Wrenbdf33762015-12-04 15:50:51 -05001871 }
1872 return importance.intValue();
1873 }
1874
1875 private String getImportanceExplanation(String key) {
1876 synchronized (this) {
1877 if (mImportanceExplanation == null) {
1878 buildImportanceExplanationLocked();
1879 }
1880 }
1881 return mImportanceExplanation.get(key);
1882 }
1883
Julia Reynoldse46bb372016-03-17 11:05:58 -04001884 private String getOverrideGroupKey(String key) {
1885 synchronized (this) {
1886 if (mOverrideGroupKeys == null) {
1887 buildOverrideGroupKeys();
1888 }
1889 }
1890 return mOverrideGroupKeys.get(key);
1891 }
1892
Julia Reynolds924eed12017-01-19 09:52:07 -05001893 private NotificationChannel getChannel(String key) {
Julia Reynolds22f02b32016-12-01 15:05:13 -05001894 synchronized (this) {
Julia Reynolds924eed12017-01-19 09:52:07 -05001895 if (mChannels == null) {
1896 buildChannelsLocked();
Julia Reynolds22f02b32016-12-01 15:05:13 -05001897 }
1898 }
Julia Reynolds924eed12017-01-19 09:52:07 -05001899 return mChannels.get(key);
Julia Reynolds22f02b32016-12-01 15:05:13 -05001900 }
1901
1902 private ArrayList<String> getOverridePeople(String key) {
1903 synchronized (this) {
1904 if (mOverridePeople == null) {
1905 buildOverridePeopleLocked();
1906 }
1907 }
1908 return mOverridePeople.get(key);
1909 }
1910
1911 private ArrayList<SnoozeCriterion> getSnoozeCriteria(String key) {
1912 synchronized (this) {
1913 if (mSnoozeCriteria == null) {
1914 buildSnoozeCriteriaLocked();
1915 }
1916 }
1917 return mSnoozeCriteria.get(key);
1918 }
1919
Julia Reynolds924eed12017-01-19 09:52:07 -05001920 private boolean getShowBadge(String key) {
1921 synchronized (this) {
1922 if (mShowBadge == null) {
1923 buildShowBadgeLocked();
1924 }
1925 }
1926 Boolean showBadge = mShowBadge.get(key);
1927 return showBadge == null ? false : showBadge.booleanValue();
1928 }
1929
Julia Reynolds503ed942017-10-04 16:04:56 -04001930 private int getUserSentiment(String key) {
1931 synchronized (this) {
1932 if (mUserSentiment == null) {
1933 buildUserSentimentLocked();
1934 }
1935 }
1936 Integer userSentiment = mUserSentiment.get(key);
1937 return userSentiment == null
1938 ? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue();
1939 }
1940
Beverly5a20a5e2018-03-06 15:02:44 -05001941 private boolean getHidden(String key) {
1942 synchronized (this) {
1943 if (mHidden == null) {
1944 buildHiddenLocked();
1945 }
1946 }
1947 Boolean hidden = mHidden.get(key);
1948 return hidden == null ? false : hidden.booleanValue();
1949 }
1950
Gus Prevas7306b902018-12-11 10:57:06 -05001951 private long getLastAudiblyAlerted(String key) {
Gus Prevasa3226492018-10-23 11:10:09 -04001952 synchronized (this) {
Gus Prevas7306b902018-12-11 10:57:06 -05001953 if (mLastAudiblyAlerted == null) {
1954 buildLastAudiblyAlertedLocked();
Gus Prevasa3226492018-10-23 11:10:09 -04001955 }
1956 }
Gus Prevas7306b902018-12-11 10:57:06 -05001957 Long lastAudibleAlerted = mLastAudiblyAlerted.get(key);
1958 return lastAudibleAlerted == null ? -1 : lastAudibleAlerted.longValue();
Gus Prevasa3226492018-10-23 11:10:09 -04001959 }
1960
Gus Prevas9abc5062018-10-31 16:11:04 -04001961 private boolean getNoisy(String key) {
1962 synchronized (this) {
1963 if (mNoisy == null) {
1964 buildNoisyLocked();
1965 }
1966 }
1967 Boolean noisy = mNoisy.get(key);
1968 return noisy == null ? false : noisy.booleanValue();
1969 }
1970
Tony Mak628cb932018-06-19 18:30:41 +01001971 private ArrayList<Notification.Action> getSmartActions(String key) {
1972 synchronized (this) {
1973 if (mSmartActions == null) {
1974 buildSmartActions();
1975 }
1976 }
1977 return mSmartActions.get(key);
1978 }
1979
Tony Makc9acf672018-07-20 13:58:24 +02001980 private ArrayList<CharSequence> getSmartReplies(String key) {
1981 synchronized (this) {
1982 if (mSmartReplies == null) {
1983 buildSmartReplies();
1984 }
1985 }
1986 return mSmartReplies.get(key);
1987 }
1988
Julia Reynolds4509ce72019-01-31 13:12:43 -05001989 private boolean canBubble(String key) {
1990 synchronized (this) {
1991 if (mRanks == null) {
1992 buildRanksLocked();
1993 }
1994 if (mCanBubble == null) {
1995 mCanBubble = mRankingUpdate.getCanBubble();
1996 }
1997 }
1998 int keyIndex = mRanks.getOrDefault(key, -1);
1999 return keyIndex >= 0 ? mCanBubble[keyIndex] : false;
2000 }
2001
Christoph Studerdda48f12014-07-29 23:13:16 +02002002 // Locked by 'this'
2003 private void buildRanksLocked() {
2004 String[] orderedKeys = mRankingUpdate.getOrderedKeys();
2005 mRanks = new ArrayMap<>(orderedKeys.length);
2006 for (int i = 0; i < orderedKeys.length; i++) {
2007 String key = orderedKeys[i];
2008 mRanks.put(key, i);
2009 }
2010 }
2011
2012 // Locked by 'this'
2013 private void buildInterceptedSetLocked() {
2014 String[] dndInterceptedKeys = mRankingUpdate.getInterceptedKeys();
2015 mIntercepted = new ArraySet<>(dndInterceptedKeys.length);
2016 Collections.addAll(mIntercepted, dndInterceptedKeys);
Christoph Studer05ad4822014-05-16 14:16:03 +02002017 }
2018
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002019 private ArrayMap<String, Integer> buildIntMapFromBundle(Bundle bundle) {
2020 ArrayMap<String, Integer> newMap = new ArrayMap<>(bundle.size());
2021 for (String key : bundle.keySet()) {
2022 newMap.put(key, bundle.getInt(key));
2023 }
2024 return newMap;
2025 }
2026
2027 private ArrayMap<String, String> buildStringMapFromBundle(Bundle bundle) {
2028 ArrayMap<String, String> newMap = new ArrayMap<>(bundle.size());
2029 for (String key : bundle.keySet()) {
2030 newMap.put(key, bundle.getString(key));
2031 }
2032 return newMap;
2033 }
2034
2035 private ArrayMap<String, Boolean> buildBooleanMapFromBundle(Bundle bundle) {
2036 ArrayMap<String, Boolean> newMap = new ArrayMap<>(bundle.size());
2037 for (String key : bundle.keySet()) {
2038 newMap.put(key, bundle.getBoolean(key));
2039 }
2040 return newMap;
2041 }
2042
Gus Prevas7306b902018-12-11 10:57:06 -05002043 private ArrayMap<String, Long> buildLongMapFromBundle(Bundle bundle) {
2044 ArrayMap<String, Long> newMap = new ArrayMap<>(bundle.size());
2045 for (String key : bundle.keySet()) {
2046 newMap.put(key, bundle.getLong(key));
2047 }
2048 return newMap;
2049 }
2050
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002051 // Locked by 'this'
2052 private void buildVisibilityOverridesLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002053 mVisibilityOverrides = buildIntMapFromBundle(mRankingUpdate.getVisibilityOverrides());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002054 }
2055
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002056 // Locked by 'this'
2057 private void buildSuppressedVisualEffectsLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002058 mSuppressedVisualEffects =
2059 buildIntMapFromBundle(mRankingUpdate.getSuppressedVisualEffects());
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002060 }
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002061
Chris Wrenbdf33762015-12-04 15:50:51 -05002062 // Locked by 'this'
2063 private void buildImportanceLocked() {
2064 String[] orderedKeys = mRankingUpdate.getOrderedKeys();
2065 int[] importance = mRankingUpdate.getImportance();
2066 mImportance = new ArrayMap<>(orderedKeys.length);
2067 for (int i = 0; i < orderedKeys.length; i++) {
2068 String key = orderedKeys[i];
2069 mImportance.put(key, importance[i]);
2070 }
2071 }
2072
2073 // Locked by 'this'
2074 private void buildImportanceExplanationLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002075 mImportanceExplanation =
2076 buildStringMapFromBundle(mRankingUpdate.getImportanceExplanation());
Chris Wrenbdf33762015-12-04 15:50:51 -05002077 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002078
Julia Reynoldse46bb372016-03-17 11:05:58 -04002079 // Locked by 'this'
2080 private void buildOverrideGroupKeys() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002081 mOverrideGroupKeys = buildStringMapFromBundle(mRankingUpdate.getOverrideGroupKeys());
Julia Reynoldse46bb372016-03-17 11:05:58 -04002082 }
2083
Julia Reynolds22f02b32016-12-01 15:05:13 -05002084 // Locked by 'this'
Julia Reynolds924eed12017-01-19 09:52:07 -05002085 private void buildChannelsLocked() {
2086 Bundle channels = mRankingUpdate.getChannels();
2087 mChannels = new ArrayMap<>(channels.size());
2088 for (String key : channels.keySet()) {
2089 mChannels.put(key, channels.getParcelable(key));
Julia Reynolds22f02b32016-12-01 15:05:13 -05002090 }
2091 }
2092
2093 // Locked by 'this'
2094 private void buildOverridePeopleLocked() {
2095 Bundle overridePeople = mRankingUpdate.getOverridePeople();
2096 mOverridePeople = new ArrayMap<>(overridePeople.size());
2097 for (String key : overridePeople.keySet()) {
2098 mOverridePeople.put(key, overridePeople.getStringArrayList(key));
2099 }
2100 }
2101
2102 // Locked by 'this'
2103 private void buildSnoozeCriteriaLocked() {
2104 Bundle snoozeCriteria = mRankingUpdate.getSnoozeCriteria();
2105 mSnoozeCriteria = new ArrayMap<>(snoozeCriteria.size());
2106 for (String key : snoozeCriteria.keySet()) {
2107 mSnoozeCriteria.put(key, snoozeCriteria.getParcelableArrayList(key));
2108 }
2109 }
2110
Julia Reynolds924eed12017-01-19 09:52:07 -05002111 // Locked by 'this'
2112 private void buildShowBadgeLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002113 mShowBadge = buildBooleanMapFromBundle(mRankingUpdate.getShowBadge());
Julia Reynolds924eed12017-01-19 09:52:07 -05002114 }
2115
Julia Reynolds503ed942017-10-04 16:04:56 -04002116 // Locked by 'this'
2117 private void buildUserSentimentLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002118 mUserSentiment = buildIntMapFromBundle(mRankingUpdate.getUserSentiment());
Julia Reynolds503ed942017-10-04 16:04:56 -04002119 }
2120
Beverly5a20a5e2018-03-06 15:02:44 -05002121 // Locked by 'this'
2122 private void buildHiddenLocked() {
Aaron Heuckrothaa01ea42018-08-14 10:05:43 -04002123 mHidden = buildBooleanMapFromBundle(mRankingUpdate.getHidden());
Beverly5a20a5e2018-03-06 15:02:44 -05002124 }
2125
Tony Mak628cb932018-06-19 18:30:41 +01002126 // Locked by 'this'
Gus Prevas7306b902018-12-11 10:57:06 -05002127 private void buildLastAudiblyAlertedLocked() {
2128 mLastAudiblyAlerted = buildLongMapFromBundle(mRankingUpdate.getLastAudiblyAlerted());
Gus Prevasa3226492018-10-23 11:10:09 -04002129 }
2130
2131 // Locked by 'this'
Gus Prevas9abc5062018-10-31 16:11:04 -04002132 private void buildNoisyLocked() {
2133 mNoisy = buildBooleanMapFromBundle(mRankingUpdate.getNoisy());
2134 }
2135
2136 // Locked by 'this'
Tony Mak628cb932018-06-19 18:30:41 +01002137 private void buildSmartActions() {
2138 Bundle smartActions = mRankingUpdate.getSmartActions();
2139 mSmartActions = new ArrayMap<>(smartActions.size());
2140 for (String key : smartActions.keySet()) {
2141 mSmartActions.put(key, smartActions.getParcelableArrayList(key));
2142 }
2143 }
2144
Tony Makc9acf672018-07-20 13:58:24 +02002145 // Locked by 'this'
2146 private void buildSmartReplies() {
2147 Bundle smartReplies = mRankingUpdate.getSmartReplies();
2148 mSmartReplies = new ArrayMap<>(smartReplies.size());
2149 for (String key : smartReplies.keySet()) {
2150 mSmartReplies.put(key, smartReplies.getCharSequenceArrayList(key));
2151 }
2152 }
2153
Christoph Studer05ad4822014-05-16 14:16:03 +02002154 // ----------- Parcelable
2155
2156 @Override
2157 public int describeContents() {
2158 return 0;
2159 }
2160
2161 @Override
2162 public void writeToParcel(Parcel dest, int flags) {
2163 dest.writeParcelable(mRankingUpdate, flags);
2164 }
2165
Christoph Studerd0694b62014-06-04 16:36:01 +02002166 public static final Creator<RankingMap> CREATOR = new Creator<RankingMap>() {
Christoph Studer05ad4822014-05-16 14:16:03 +02002167 @Override
Christoph Studerd0694b62014-06-04 16:36:01 +02002168 public RankingMap createFromParcel(Parcel source) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002169 NotificationRankingUpdate rankingUpdate = source.readParcelable(null);
Christoph Studerd0694b62014-06-04 16:36:01 +02002170 return new RankingMap(rankingUpdate);
Christoph Studer05ad4822014-05-16 14:16:03 +02002171 }
2172
2173 @Override
Christoph Studerd0694b62014-06-04 16:36:01 +02002174 public RankingMap[] newArray(int size) {
2175 return new RankingMap[size];
Christoph Studer05ad4822014-05-16 14:16:03 +02002176 }
2177 };
Daniel Sandler5feceeb2013-03-22 18:29:23 -07002178 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002179
2180 private final class MyHandler extends Handler {
2181 public static final int MSG_ON_NOTIFICATION_POSTED = 1;
2182 public static final int MSG_ON_NOTIFICATION_REMOVED = 2;
2183 public static final int MSG_ON_LISTENER_CONNECTED = 3;
2184 public static final int MSG_ON_NOTIFICATION_RANKING_UPDATE = 4;
2185 public static final int MSG_ON_LISTENER_HINTS_CHANGED = 5;
2186 public static final int MSG_ON_INTERRUPTION_FILTER_CHANGED = 6;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002187 public static final int MSG_ON_NOTIFICATION_CHANNEL_MODIFIED = 7;
2188 public static final int MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED = 8;
Julia Reynolds12ad7ca2019-01-28 09:29:16 -05002189 public static final int MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED = 9;
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002190
2191 public MyHandler(Looper looper) {
2192 super(looper, null, false);
2193 }
2194
2195 @Override
2196 public void handleMessage(Message msg) {
Chris Wren5717bd62016-04-06 18:15:46 -04002197 if (!isConnected) {
2198 return;
2199 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002200 switch (msg.what) {
2201 case MSG_ON_NOTIFICATION_POSTED: {
2202 SomeArgs args = (SomeArgs) msg.obj;
2203 StatusBarNotification sbn = (StatusBarNotification) args.arg1;
2204 RankingMap rankingMap = (RankingMap) args.arg2;
2205 args.recycle();
2206 onNotificationPosted(sbn, rankingMap);
2207 } break;
2208
2209 case MSG_ON_NOTIFICATION_REMOVED: {
2210 SomeArgs args = (SomeArgs) msg.obj;
2211 StatusBarNotification sbn = (StatusBarNotification) args.arg1;
2212 RankingMap rankingMap = (RankingMap) args.arg2;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05002213 int reason = (int) args.arg3;
Julia Reynolds503ed942017-10-04 16:04:56 -04002214 NotificationStats stats = (NotificationStats) args.arg4;
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002215 args.recycle();
Julia Reynolds503ed942017-10-04 16:04:56 -04002216 onNotificationRemoved(sbn, rankingMap, stats, reason);
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002217 } break;
2218
2219 case MSG_ON_LISTENER_CONNECTED: {
2220 onListenerConnected();
2221 } break;
2222
2223 case MSG_ON_NOTIFICATION_RANKING_UPDATE: {
2224 RankingMap rankingMap = (RankingMap) msg.obj;
2225 onNotificationRankingUpdate(rankingMap);
2226 } break;
2227
2228 case MSG_ON_LISTENER_HINTS_CHANGED: {
2229 final int hints = msg.arg1;
2230 onListenerHintsChanged(hints);
2231 } break;
2232
2233 case MSG_ON_INTERRUPTION_FILTER_CHANGED: {
2234 final int interruptionFilter = msg.arg1;
2235 onInterruptionFilterChanged(interruptionFilter);
2236 } break;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002237
2238 case MSG_ON_NOTIFICATION_CHANNEL_MODIFIED: {
2239 SomeArgs args = (SomeArgs) msg.obj;
2240 String pkgName = (String) args.arg1;
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002241 UserHandle user= (UserHandle) args.arg2;
2242 NotificationChannel channel = (NotificationChannel) args.arg3;
2243 int modificationType = (int) args.arg4;
2244 onNotificationChannelModified(pkgName, user, channel, modificationType);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002245 } break;
2246
2247 case MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED: {
2248 SomeArgs args = (SomeArgs) msg.obj;
2249 String pkgName = (String) args.arg1;
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002250 UserHandle user = (UserHandle) args.arg2;
2251 NotificationChannelGroup group = (NotificationChannelGroup) args.arg3;
2252 int modificationType = (int) args.arg4;
2253 onNotificationChannelGroupModified(pkgName, user, group, modificationType);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002254 } break;
Julia Reynolds12ad7ca2019-01-28 09:29:16 -05002255
2256 case MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED: {
2257 onStatusBarIconsBehaviorChanged((Boolean) msg.obj);
2258 } break;
Svet Ganovb8f53ee2016-02-18 08:38:56 -08002259 }
2260 }
2261 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -07002262}