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