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