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