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