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