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