1 /* 2 * Copyright (C) 2014 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 androidx.core.app; 18 19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX; 20 21 import android.Manifest; 22 import android.app.AppOpsManager; 23 import android.app.Notification; 24 import android.app.NotificationChannel; 25 import android.app.NotificationChannelGroup; 26 import android.app.NotificationManager; 27 import android.app.PendingIntent; 28 import android.app.Service; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.ServiceConnection; 33 import android.content.pm.ApplicationInfo; 34 import android.content.pm.PackageManager; 35 import android.content.pm.ResolveInfo; 36 import android.content.pm.ShortcutInfo; 37 import android.os.Build; 38 import android.os.Bundle; 39 import android.os.DeadObjectException; 40 import android.os.Handler; 41 import android.os.HandlerThread; 42 import android.os.IBinder; 43 import android.os.Message; 44 import android.os.RemoteException; 45 import android.provider.Settings; 46 import android.service.notification.NotificationListenerService; 47 import android.service.notification.StatusBarNotification; 48 import android.support.v4.app.INotificationSideChannel; 49 import android.util.Log; 50 51 import androidx.annotation.GuardedBy; 52 import androidx.annotation.IntDef; 53 import androidx.annotation.RequiresApi; 54 import androidx.annotation.RequiresPermission; 55 import androidx.annotation.RestrictTo; 56 import androidx.annotation.VisibleForTesting; 57 58 import org.jspecify.annotations.NonNull; 59 import org.jspecify.annotations.Nullable; 60 61 import java.lang.annotation.Retention; 62 import java.lang.annotation.RetentionPolicy; 63 import java.lang.reflect.Field; 64 import java.lang.reflect.InvocationTargetException; 65 import java.lang.reflect.Method; 66 import java.util.ArrayDeque; 67 import java.util.ArrayList; 68 import java.util.Arrays; 69 import java.util.Collection; 70 import java.util.Collections; 71 import java.util.HashMap; 72 import java.util.HashSet; 73 import java.util.Iterator; 74 import java.util.List; 75 import java.util.Map; 76 import java.util.Set; 77 78 /** 79 * Compatibility library for NotificationManager with fallbacks for older platforms. 80 * 81 * <p>To use this class, call the static function {@link #from} to get a 82 * {@link NotificationManagerCompat} object, and then call one of its 83 * methods to post or cancel notifications. 84 */ 85 public final class NotificationManagerCompat { 86 private static final String TAG = "NotifManCompat"; 87 private static final String CHECK_OP_NO_THROW = "checkOpNoThrow"; 88 private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION"; 89 90 /** 91 * Notification extras key: if set to true, the posted notification should use 92 * the side channel for delivery instead of using notification manager. 93 */ 94 public static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel"; 95 96 /** 97 * Intent action to register for on a service to receive side channel 98 * notifications. The listening service must be in the same package as an enabled 99 * {@link NotificationListenerService}. 100 */ 101 public static final String ACTION_BIND_SIDE_CHANNEL = 102 "android.support.BIND_NOTIFICATION_SIDE_CHANNEL"; 103 104 /** 105 * Maximum sdk build version which needs support for side channeled notifications. 106 * Currently the only needed use is for side channeling group children before KITKAT_WATCH. 107 */ 108 static final int MAX_SIDE_CHANNEL_SDK_VERSION = 19; 109 110 /** Base time delay for a side channel listener queue retry. */ 111 private static final int SIDE_CHANNEL_RETRY_BASE_INTERVAL_MS = 1000; 112 /** Maximum retries for a side channel listener before dropping tasks. */ 113 private static final int SIDE_CHANNEL_RETRY_MAX_COUNT = 6; 114 /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */ 115 private static final String SETTING_ENABLED_NOTIFICATION_LISTENERS = 116 "enabled_notification_listeners"; 117 118 /** Cache of enabled notification listener components */ 119 private static final Object sEnabledNotificationListenersLock = new Object(); 120 @GuardedBy("sEnabledNotificationListenersLock") 121 private static String sEnabledNotificationListeners; 122 @GuardedBy("sEnabledNotificationListenersLock") 123 private static Set<String> sEnabledNotificationListenerPackages = new HashSet<String>(); 124 125 private final Context mContext; 126 private final NotificationManager mNotificationManager; 127 /** Lock for mutable static fields */ 128 private static final Object sLock = new Object(); 129 @GuardedBy("sLock") 130 private static SideChannelManager sSideChannelManager; 131 132 @RestrictTo(LIBRARY_GROUP_PREFIX) 133 @IntDef({INTERRUPTION_FILTER_UNKNOWN, INTERRUPTION_FILTER_ALL, INTERRUPTION_FILTER_PRIORITY, 134 INTERRUPTION_FILTER_NONE, INTERRUPTION_FILTER_ALARMS}) 135 @Retention(RetentionPolicy.SOURCE) 136 public @interface InterruptionFilter { 137 } 138 139 /** 140 * {@link #getCurrentInterruptionFilter() Interruption filter} constant - 141 * Normal interruption filter - no notifications are suppressed. 142 */ 143 public static final int INTERRUPTION_FILTER_ALL = 1; 144 145 /** 146 * {@link #getCurrentInterruptionFilter() Interruption filter} constant - 147 * Priority interruption filter - all notifications are suppressed except those that match 148 * the priority criteria. Some audio streams are muted. See 149 * {@link Policy#priorityCallSenders}, {@link Policy#priorityCategories}, 150 * {@link Policy#priorityMessageSenders} to define or query this criteria. Users can 151 * additionally specify packages that can bypass this interruption filter. 152 */ 153 public static final int INTERRUPTION_FILTER_PRIORITY = 2; 154 155 /** 156 * {@link #getCurrentInterruptionFilter() Interruption filter} constant - 157 * No interruptions filter - all notifications are suppressed and all audio streams (except 158 * those used for phone calls) and vibrations are muted. 159 */ 160 public static final int INTERRUPTION_FILTER_NONE = 3; 161 162 /** 163 * {@link #getCurrentInterruptionFilter() Interruption filter} constant - 164 * Alarms only interruption filter - all notifications except those of category 165 * {@link Notification#CATEGORY_ALARM} are suppressed. Some audio streams are muted. 166 */ 167 public static final int INTERRUPTION_FILTER_ALARMS = 4; 168 169 /** {@link #getCurrentInterruptionFilter() Interruption filter} constant - 170 * returned when the value is unavailable for any reason. 171 */ 172 public static final int INTERRUPTION_FILTER_UNKNOWN = 0; 173 174 /** 175 * Value signifying that the user has not expressed an importance. 176 * 177 * This value is for persisting preferences, and should never be associated with 178 * an actual notification. 179 */ 180 public static final int IMPORTANCE_UNSPECIFIED = -1000; 181 182 /** 183 * A notification with no importance: shows nowhere, is blocked. 184 */ 185 public static final int IMPORTANCE_NONE = 0; 186 187 /** 188 * Min notification importance: only shows in the shade, below the fold. 189 */ 190 public static final int IMPORTANCE_MIN = 1; 191 192 /** 193 * Low notification importance: shows everywhere, but is not intrusive. 194 */ 195 public static final int IMPORTANCE_LOW = 2; 196 197 /** 198 * Default notification importance: shows everywhere, allowed to makes noise, 199 * but does not visually intrude. 200 */ 201 public static final int IMPORTANCE_DEFAULT = 3; 202 203 /** 204 * Higher notification importance: shows everywhere, allowed to makes noise and peek. 205 */ 206 public static final int IMPORTANCE_HIGH = 4; 207 208 /** 209 * Highest notification importance: shows everywhere, allowed to makes noise, peek, and 210 * use full screen intents. 211 */ 212 public static final int IMPORTANCE_MAX = 5; 213 214 /** Get a {@link NotificationManagerCompat} instance for a provided context. */ from(@onNull Context context)215 public static @NonNull NotificationManagerCompat from(@NonNull Context context) { 216 return new NotificationManagerCompat(context); 217 } 218 NotificationManagerCompat(Context context)219 private NotificationManagerCompat(Context context) { 220 mContext = context; 221 mNotificationManager = (NotificationManager) mContext.getSystemService( 222 Context.NOTIFICATION_SERVICE); 223 } 224 225 @VisibleForTesting NotificationManagerCompat(@onNull NotificationManager notificationManager, @NonNull Context context)226 NotificationManagerCompat(@NonNull NotificationManager notificationManager, 227 @NonNull Context context) { 228 mContext = context; 229 mNotificationManager = notificationManager; 230 } 231 232 /** 233 * Cancel a previously shown notification. 234 * 235 * @param id the ID of the notification 236 */ cancel(int id)237 public void cancel(int id) { 238 cancel(null, id); 239 } 240 241 /** 242 * Cancel a previously shown notification. 243 * 244 * @param tag the string identifier of the notification. 245 * @param id the ID of the notification 246 */ cancel(@ullable String tag, int id)247 public void cancel(@Nullable String tag, int id) { 248 mNotificationManager.cancel(tag, id); 249 if (Build.VERSION.SDK_INT <= MAX_SIDE_CHANNEL_SDK_VERSION) { 250 pushSideChannelQueue(new CancelTask(mContext.getPackageName(), id, tag)); 251 } 252 } 253 254 /** Cancel all previously shown notifications. */ cancelAll()255 public void cancelAll() { 256 mNotificationManager.cancelAll(); 257 if (Build.VERSION.SDK_INT <= MAX_SIDE_CHANNEL_SDK_VERSION) { 258 pushSideChannelQueue(new CancelTask(mContext.getPackageName())); 259 } 260 } 261 262 /** 263 * Post a notification to be shown in the status bar, stream, etc. 264 * 265 * @param id the ID of the notification 266 * @param notification the notification to post to the system 267 */ 268 @RequiresPermission(Manifest.permission.POST_NOTIFICATIONS) notify(int id, @NonNull Notification notification)269 public void notify(int id, @NonNull Notification notification) { 270 notify(null, id, notification); 271 } 272 273 /** 274 * Post a notification to be shown in the status bar, stream, etc. 275 * 276 * @param tag the string identifier for a notification. Can be {@code null}. 277 * @param id the ID of the notification. The pair (tag, id) must be unique within 278 * your app. 279 * @param notification the notification to post to the system 280 */ 281 @RequiresPermission(Manifest.permission.POST_NOTIFICATIONS) notify(@ullable String tag, int id, @NonNull Notification notification)282 public void notify(@Nullable String tag, int id, @NonNull Notification notification) { 283 if (useSideChannelForNotification(notification)) { 284 pushSideChannelQueue(new NotifyTask(mContext.getPackageName(), id, tag, notification)); 285 // Cancel this notification in notification manager if it just transitioned to being 286 // side channelled. 287 mNotificationManager.cancel(tag, id); 288 } else { 289 mNotificationManager.notify(tag, id, notification); 290 } 291 } 292 293 /** 294 * Post a number of notifications, to be shown in the status bar, stream, etc. 295 * Each notification will attempt to be posted in the order provided in the {@code 296 * notificationWithIds} list. Each notification must have a provided id and may have a 297 * provided tag. 298 * 299 * This is the preferred method for posting groups of notifications, to improve sound and 300 * animation behavior. 301 */ 302 @RequiresPermission(Manifest.permission.POST_NOTIFICATIONS) notify(@onNull List<NotificationWithIdAndTag> notificationWithIdAndTags)303 public void notify(@NonNull List<NotificationWithIdAndTag> notificationWithIdAndTags) { 304 final int notificationsSize = notificationWithIdAndTags.size(); 305 for (int i = 0; i < notificationsSize; i++) { 306 NotificationWithIdAndTag notificationWithIdAndTag = notificationWithIdAndTags.get(i); 307 notify(notificationWithIdAndTag.mTag, notificationWithIdAndTag.mId, 308 notificationWithIdAndTag.mNotification); 309 } 310 } 311 312 /** 313 * Helper class which encapsulates a Notification, its id, and optionally a tag, for use when 314 * batch-posting a number of notifications. 315 */ 316 public static class NotificationWithIdAndTag { 317 final String mTag; 318 final int mId; 319 Notification mNotification; 320 NotificationWithIdAndTag(@ullable String tag, int id, @NonNull Notification notification)321 public NotificationWithIdAndTag(@Nullable String tag, int id, 322 @NonNull Notification notification) { 323 this.mTag = tag; 324 this.mId = id; 325 this.mNotification = notification; 326 } 327 NotificationWithIdAndTag(int id, @NonNull Notification notification)328 public NotificationWithIdAndTag(int id, @NonNull Notification notification) { 329 this(null, id, notification); 330 } 331 } 332 333 /** 334 * Recover a list of active notifications: ones that have been posted by the calling app that 335 * have not yet been dismissed by the user or {@link #cancel(String, int)}ed by the app. 336 * 337 * <p><Each notification is embedded in a {@link StatusBarNotification} object, including the 338 * original <code>tag</code> and <code>id</code> supplied to 339 * {@link #notify(String, int, Notification) notify()} 340 * (via {@link StatusBarNotification#getTag() getTag()} and 341 * {@link StatusBarNotification#getId() getId()}) as well as a copy of the original 342 * {@link Notification} object (via {@link StatusBarNotification#getNotification()}). 343 * </p> 344 * <p>From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as an 345 * app's notification delegate via 346 * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}. 347 * </p> 348 * <p> 349 * Returns an empty list on {@link Build.VERSION_CODES#LOLLIPOP_MR1} and earlier. 350 * </p> 351 * 352 * @return A list of {@link StatusBarNotification}. 353 */ getActiveNotifications()354 public @NonNull List<StatusBarNotification> getActiveNotifications() { 355 if (Build.VERSION.SDK_INT >= 23) { 356 return Api23Impl.getActiveNotifications(mNotificationManager); 357 } else { 358 return new ArrayList<>(); 359 } 360 } 361 362 /** 363 * Returns whether notifications from the calling package are not blocked. 364 */ areNotificationsEnabled()365 public boolean areNotificationsEnabled() { 366 if (Build.VERSION.SDK_INT >= 24) { 367 return Api24Impl.areNotificationsEnabled(mNotificationManager); 368 } else { 369 AppOpsManager appOps = 370 (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 371 ApplicationInfo appInfo = mContext.getApplicationInfo(); 372 String pkg = mContext.getApplicationContext().getPackageName(); 373 int uid = appInfo.uid; 374 try { 375 Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName()); 376 Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, 377 Integer.TYPE, String.class); 378 Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION); 379 int value = (int) opPostNotificationValue.get(Integer.class); 380 return ((int) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg) 381 == AppOpsManager.MODE_ALLOWED); 382 } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException 383 | InvocationTargetException | IllegalAccessException | RuntimeException e) { 384 return true; 385 } 386 } 387 } 388 389 /** 390 * Returns the user specified importance for notifications from the calling package. 391 * 392 * @return An importance level, such as {@link #IMPORTANCE_DEFAULT}. 393 */ getImportance()394 public int getImportance() { 395 if (Build.VERSION.SDK_INT >= 24) { 396 return Api24Impl.getImportance(mNotificationManager); 397 } else { 398 return IMPORTANCE_UNSPECIFIED; 399 } 400 } 401 402 /** 403 * Creates a notification channel that notifications can be posted to. 404 * 405 * This can also be used to restore a deleted channel and to update an existing channel's 406 * name, description, group, and/or importance. 407 * 408 * <p>The importance of an existing channel will only be changed if the new importance is lower 409 * than the current value and the user has not altered any settings on this channel. 410 * 411 * <p>The group of an existing channel will only be changed if the channel does not already 412 * belong to a group. 413 * 414 * All other fields are ignored for channels that already exist. 415 * 416 * It doesn't do anything on older SDKs which don't support Notification Channels. 417 * 418 * @param channel the channel to create. Note that the created channel may differ from this 419 * value. If the provided channel is malformed, a RemoteException will be 420 * thrown. 421 */ createNotificationChannel(@onNull NotificationChannel channel)422 public void createNotificationChannel(@NonNull NotificationChannel channel) { 423 if (Build.VERSION.SDK_INT >= 26) { 424 Api26Impl.createNotificationChannel(mNotificationManager, channel); 425 } 426 } 427 428 /** 429 * Creates a notification channel that notifications can be posted to. 430 * 431 * This can also be used to restore a deleted channel and to update an existing channel's 432 * name, description, group, and/or importance. 433 * 434 * <p>The importance of an existing channel will only be changed if the new importance is lower 435 * than the current value and the user has not altered any settings on this channel. 436 * 437 * <p>The group of an existing channel will only be changed if the channel does not already 438 * belong to a group. 439 * 440 * All other fields are ignored for channels that already exist. 441 * 442 * It doesn't do anything on older SDKs which don't support Notification Channels. 443 * 444 * @param channel the channel to create. Note that the created channel may differ from this 445 * value. If the provided channel is malformed, a RemoteException will be 446 * thrown. 447 */ createNotificationChannel(@onNull NotificationChannelCompat channel)448 public void createNotificationChannel(@NonNull NotificationChannelCompat channel) { 449 createNotificationChannel(channel.getNotificationChannel()); 450 } 451 452 /** 453 * Creates a group container for {@link NotificationChannel} objects. 454 * 455 * This can be used to rename an existing group. 456 * 457 * It doesn't do anything on older SDKs which don't support Notification Channels. 458 * 459 * @param group The group to create 460 */ createNotificationChannelGroup(@onNull NotificationChannelGroup group)461 public void createNotificationChannelGroup(@NonNull NotificationChannelGroup group) { 462 if (Build.VERSION.SDK_INT >= 26) { 463 Api26Impl.createNotificationChannelGroup(mNotificationManager, group); 464 } 465 } 466 467 /** 468 * Creates a group container for {@link NotificationChannel} objects. 469 * 470 * This can be used to rename an existing group. 471 * 472 * It doesn't do anything on older SDKs which don't support Notification Channels. 473 * 474 * @param group The group to create 475 */ createNotificationChannelGroup(@onNull NotificationChannelGroupCompat group)476 public void createNotificationChannelGroup(@NonNull NotificationChannelGroupCompat group) { 477 createNotificationChannelGroup(group.getNotificationChannelGroup()); 478 } 479 480 /** 481 * Creates multiple notification channels that different notifications can be posted to. See 482 * {@link #createNotificationChannel(NotificationChannel)}. 483 * 484 * It doesn't do anything on older SDKs which don't support Notification Channels. 485 * 486 * @param channels the list of channels to attempt to create. 487 */ createNotificationChannels(@onNull List<NotificationChannel> channels)488 public void createNotificationChannels(@NonNull List<NotificationChannel> channels) { 489 if (Build.VERSION.SDK_INT >= 26) { 490 Api26Impl.createNotificationChannels(mNotificationManager, channels); 491 } 492 } 493 494 /** 495 * Creates multiple notification channels that different notifications can be posted to. See 496 * {@link #createNotificationChannel(NotificationChannelCompat)}. 497 * 498 * It doesn't do anything on older SDKs which don't support Notification Channels. 499 * 500 * @param channels the list of channels to attempt to create. 501 */ createNotificationChannelsCompat( @onNull List<NotificationChannelCompat> channels)502 public void createNotificationChannelsCompat( 503 @NonNull List<NotificationChannelCompat> channels) { 504 if (Build.VERSION.SDK_INT >= 26 && !channels.isEmpty()) { 505 List<NotificationChannel> platformChannels = new ArrayList<>(channels.size()); 506 for (NotificationChannelCompat channel : channels) { 507 platformChannels.add(channel.getNotificationChannel()); 508 } 509 Api26Impl.createNotificationChannels(mNotificationManager, platformChannels); 510 } 511 } 512 513 /** 514 * Creates multiple notification channel groups. See 515 * {@link #createNotificationChannelGroup(NotificationChannelGroup)}. 516 * 517 * It doesn't do anything on older SDKs which don't support Notification Channels. 518 * 519 * @param groups The list of groups to create 520 */ createNotificationChannelGroups(@onNull List<NotificationChannelGroup> groups)521 public void createNotificationChannelGroups(@NonNull List<NotificationChannelGroup> groups) { 522 if (Build.VERSION.SDK_INT >= 26) { 523 Api26Impl.createNotificationChannelGroups(mNotificationManager, groups); 524 } 525 } 526 527 /** 528 * Creates multiple notification channel groups. See 529 * {@link #createNotificationChannelGroup(NotificationChannelGroupCompat)}. 530 * 531 * It doesn't do anything on older SDKs which don't support Notification Channels. 532 * 533 * @param groups The list of groups to create 534 */ createNotificationChannelGroupsCompat( @onNull List<NotificationChannelGroupCompat> groups)535 public void createNotificationChannelGroupsCompat( 536 @NonNull List<NotificationChannelGroupCompat> groups) { 537 if (Build.VERSION.SDK_INT >= 26 && !groups.isEmpty()) { 538 List<NotificationChannelGroup> platformGroups = new ArrayList<>(groups.size()); 539 for (NotificationChannelGroupCompat group : groups) { 540 platformGroups.add(group.getNotificationChannelGroup()); 541 } 542 Api26Impl.createNotificationChannelGroups(mNotificationManager, platformGroups); 543 } 544 } 545 546 /** 547 * Deletes the given notification channel. 548 * 549 * <p>If you {@link #createNotificationChannel(NotificationChannel) create} a new channel with 550 * this same id, the deleted channel will be un-deleted with all of the same settings it 551 * had before it was deleted. 552 * 553 * It doesn't do anything on older SDKs which don't support Notification Channels. 554 */ deleteNotificationChannel(@onNull String channelId)555 public void deleteNotificationChannel(@NonNull String channelId) { 556 if (Build.VERSION.SDK_INT >= 26) { 557 Api26Impl.deleteNotificationChannel(mNotificationManager, channelId); 558 } 559 } 560 561 /** 562 * Deletes the given notification channel group, and all notification channels that 563 * belong to it. 564 * 565 * It doesn't do anything on older SDKs which don't support Notification Channels. 566 */ deleteNotificationChannelGroup(@onNull String groupId)567 public void deleteNotificationChannelGroup(@NonNull String groupId) { 568 if (Build.VERSION.SDK_INT >= 26) { 569 Api26Impl.deleteNotificationChannelGroup(mNotificationManager, groupId); 570 } 571 } 572 573 /** 574 * Deletes notification channels for which ids are NOT given. 575 * 576 * This will NOT delete channels which are conversation children of the given channels. 577 * 578 * It doesn't do anything on older SDKs which don't support Notification Channels. 579 * 580 * @param channelIds the IDs of any channels which should NOT be deleted by this method. 581 */ deleteUnlistedNotificationChannels(@onNull Collection<String> channelIds)582 public void deleteUnlistedNotificationChannels(@NonNull Collection<String> channelIds) { 583 if (Build.VERSION.SDK_INT >= 26) { 584 for (NotificationChannel channel : 585 Api26Impl.getNotificationChannels(mNotificationManager)) { 586 if (channelIds.contains(Api26Impl.getId(channel))) { 587 continue; 588 } 589 if (Build.VERSION.SDK_INT >= 30 590 && channelIds.contains(Api30Impl.getParentChannelId(channel))) { 591 continue; 592 } 593 Api26Impl.deleteNotificationChannel(mNotificationManager, 594 Api26Impl.getId(channel)); 595 } 596 } 597 } 598 599 /** 600 * Returns the notification channel settings for a given channel id. 601 * 602 * Returns {@code null} on older SDKs which don't support Notification Channels. 603 */ getNotificationChannel(@onNull String channelId)604 public @Nullable NotificationChannel getNotificationChannel(@NonNull String channelId) { 605 if (Build.VERSION.SDK_INT >= 26) { 606 return Api26Impl.getNotificationChannel(mNotificationManager, channelId); 607 } 608 return null; 609 } 610 611 /** 612 * Returns the notification channel settings for a given channel id. 613 * 614 * Returns {@code null} on older SDKs which don't support Notification Channels. 615 */ getNotificationChannelCompat( @onNull String channelId)616 public @Nullable NotificationChannelCompat getNotificationChannelCompat( 617 @NonNull String channelId) { 618 if (Build.VERSION.SDK_INT >= 26) { 619 NotificationChannel channel = getNotificationChannel(channelId); 620 if (channel != null) { 621 return new NotificationChannelCompat(channel); 622 } 623 } 624 return null; 625 } 626 627 /** 628 * Returns the notification channel settings for a given channel and 629 * {@link ShortcutInfo#getId() conversation id}. 630 * 631 * Returns the channel for the channelId on older SDKs which don't support Conversations. 632 * 633 * Returns {@code null} on older SDKs which don't support Notification Channels. 634 */ getNotificationChannel(@onNull String channelId, @NonNull String conversationId)635 public @Nullable NotificationChannel getNotificationChannel(@NonNull String channelId, 636 @NonNull String conversationId) { 637 if (Build.VERSION.SDK_INT >= 30) { 638 return Api30Impl.getNotificationChannel(mNotificationManager, channelId, 639 conversationId); 640 } 641 return getNotificationChannel(channelId); 642 } 643 644 /** 645 * Returns the notification channel settings for a given channel and 646 * {@link ShortcutInfo#getId() conversation id}. 647 * 648 * Returns the channel for the channelId on older SDKs which don't support Conversations. 649 * 650 * Returns {@code null} on older SDKs which don't support Notification Channels. 651 */ getNotificationChannelCompat( @onNull String channelId, @NonNull String conversationId)652 public @Nullable NotificationChannelCompat getNotificationChannelCompat( 653 @NonNull String channelId, @NonNull String conversationId) { 654 if (Build.VERSION.SDK_INT >= 26) { 655 NotificationChannel channel = getNotificationChannel(channelId, conversationId); 656 if (channel != null) { 657 return new NotificationChannelCompat(channel); 658 } 659 } 660 return null; 661 } 662 663 /** 664 * Returns the notification channel group settings for a given channel group id. 665 * 666 * Returns {@code null} on older SDKs which don't support Notification Channels. 667 */ getNotificationChannelGroup( @onNull String channelGroupId)668 public @Nullable NotificationChannelGroup getNotificationChannelGroup( 669 @NonNull String channelGroupId) { 670 if (Build.VERSION.SDK_INT >= 28) { 671 return Api28Impl.getNotificationChannelGroup(mNotificationManager, channelGroupId); 672 } else if (Build.VERSION.SDK_INT >= 26) { 673 // find the group in list by its ID 674 for (NotificationChannelGroup group : getNotificationChannelGroups()) { 675 if (Api26Impl.getId(group).equals(channelGroupId)) return group; 676 } 677 // requested group doesn't exist 678 return null; 679 } else { 680 return null; 681 } 682 } 683 684 /** 685 * Returns the notification channel group settings for a given channel group id. 686 * 687 * Returns {@code null} on older SDKs which don't support Notification Channels. 688 */ getNotificationChannelGroupCompat( @onNull String channelGroupId)689 public @Nullable NotificationChannelGroupCompat getNotificationChannelGroupCompat( 690 @NonNull String channelGroupId) { 691 if (Build.VERSION.SDK_INT >= 28) { 692 NotificationChannelGroup group = getNotificationChannelGroup(channelGroupId); 693 if (group != null) { 694 return new NotificationChannelGroupCompat(group); 695 } 696 } else if (Build.VERSION.SDK_INT >= 26) { 697 NotificationChannelGroup group = getNotificationChannelGroup(channelGroupId); 698 if (group != null) { 699 return new NotificationChannelGroupCompat(group, getNotificationChannels()); 700 } 701 } 702 return null; 703 } 704 705 /** 706 * Returns all notification channels belonging to the calling app 707 * or an empty list on older SDKs which don't support Notification Channels. 708 */ getNotificationChannels()709 public @NonNull List<NotificationChannel> getNotificationChannels() { 710 if (Build.VERSION.SDK_INT >= 26) { 711 return Api26Impl.getNotificationChannels(mNotificationManager); 712 } 713 return Collections.emptyList(); 714 } 715 716 /** 717 * Returns all notification channels belonging to the calling app 718 * or an empty list on older SDKs which don't support Notification Channels. 719 */ 720 @SuppressWarnings("MixedMutabilityReturnType") getNotificationChannelsCompat()721 public @NonNull List<NotificationChannelCompat> getNotificationChannelsCompat() { 722 if (Build.VERSION.SDK_INT >= 26) { 723 List<NotificationChannel> channels = getNotificationChannels(); 724 if (!channels.isEmpty()) { 725 List<NotificationChannelCompat> channelsCompat = new ArrayList<>(channels.size()); 726 for (NotificationChannel channel : channels) { 727 channelsCompat.add(new NotificationChannelCompat(channel)); 728 } 729 return channelsCompat; 730 } 731 } 732 return Collections.emptyList(); 733 } 734 735 /** 736 * Returns all notification channel groups belonging to the calling app 737 * or an empty list on older SDKs which don't support Notification Channels. 738 */ getNotificationChannelGroups()739 public @NonNull List<NotificationChannelGroup> getNotificationChannelGroups() { 740 if (Build.VERSION.SDK_INT >= 26) { 741 return Api26Impl.getNotificationChannelGroups(mNotificationManager); 742 } 743 return Collections.emptyList(); 744 } 745 746 /** 747 * Returns all notification channel groups belonging to the calling app 748 * or an empty list on older SDKs which don't support Notification Channels. 749 */ 750 @SuppressWarnings("MixedMutabilityReturnType") getNotificationChannelGroupsCompat()751 public @NonNull List<NotificationChannelGroupCompat> getNotificationChannelGroupsCompat() { 752 if (Build.VERSION.SDK_INT >= 26) { 753 List<NotificationChannelGroup> groups = getNotificationChannelGroups(); 754 if (!groups.isEmpty()) { 755 // Don't query getNotificationChannels() on API 28+ where it isn't needed 756 List<NotificationChannel> allChannels = Build.VERSION.SDK_INT >= 28 757 ? Collections.<NotificationChannel>emptyList() 758 : getNotificationChannels(); 759 List<NotificationChannelGroupCompat> groupsCompat = new ArrayList<>(groups.size()); 760 for (NotificationChannelGroup group : groups) { 761 if (Build.VERSION.SDK_INT >= 28) { 762 groupsCompat.add(new NotificationChannelGroupCompat(group)); 763 } else { 764 groupsCompat.add(new NotificationChannelGroupCompat(group, allChannels)); 765 } 766 } 767 return groupsCompat; 768 } 769 } 770 return Collections.emptyList(); 771 } 772 773 /** 774 * Get the set of packages that have an enabled notification listener component within them. 775 */ getEnabledListenerPackages(@onNull Context context)776 public static @NonNull Set<String> getEnabledListenerPackages(@NonNull Context context) { 777 final String enabledNotificationListeners = Settings.Secure.getString( 778 context.getContentResolver(), 779 SETTING_ENABLED_NOTIFICATION_LISTENERS); 780 synchronized (sEnabledNotificationListenersLock) { 781 // Parse the string again if it is different from the last time this method was called. 782 if (enabledNotificationListeners != null 783 && !enabledNotificationListeners.equals(sEnabledNotificationListeners)) { 784 final String[] components = enabledNotificationListeners.split(":", -1); 785 Set<String> packageNames = new HashSet<String>(components.length); 786 for (String component : components) { 787 ComponentName componentName = ComponentName.unflattenFromString(component); 788 if (componentName != null) { 789 packageNames.add(componentName.getPackageName()); 790 } 791 } 792 sEnabledNotificationListenerPackages = packageNames; 793 sEnabledNotificationListeners = enabledNotificationListeners; 794 } 795 return sEnabledNotificationListenerPackages; 796 } 797 } 798 799 /** 800 * Returns whether the calling app can send fullscreen intents. 801 * 802 * <p>Fullscreen intents were introduced in Android 803 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, where apps could always attach a full 804 * screen intent to their notification via 805 * {@link Notification.Builder#setFullScreenIntent(PendingIntent, boolean)}}. 806 * 807 * <p>Android {@link android.os.Build.VERSION_CODES#Q} introduced the 808 * {@link android.Manifest.permission#USE_FULL_SCREEN_INTENT} 809 * permission, where SystemUI will only show the full screen intent attached to a notification 810 * if the permission is declared in the manifest. 811 * 812 * <p>Starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, apps 813 * may not have permission to use {@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}. If 814 * the FSI permission is denied, SystemUI will show the notification as an expanded heads up 815 * notification on lockscreen. 816 * 817 * <p>To request access, add the {@link android.Manifest.permission#USE_FULL_SCREEN_INTENT} 818 * permission to your manifest, and use 819 * {@link android.provider.Settings#ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT} to send the user 820 * to the settings page where they can grant your app the FSI permission. 821 */ canUseFullScreenIntent()822 public boolean canUseFullScreenIntent() { 823 if (Build.VERSION.SDK_INT < 29) { 824 return true; 825 } 826 if (Build.VERSION.SDK_INT < 34) { 827 final int permissionState = 828 mContext.checkSelfPermission(Manifest.permission.USE_FULL_SCREEN_INTENT); 829 return permissionState == PackageManager.PERMISSION_GRANTED; 830 } 831 return Api34Impl.canUseFullScreenIntent(mNotificationManager); 832 } 833 834 /** 835 * Returns true if this notification should use the side channel for delivery. 836 */ useSideChannelForNotification(Notification notification)837 private static boolean useSideChannelForNotification(Notification notification) { 838 Bundle extras = NotificationCompat.getExtras(notification); 839 return extras != null && extras.getBoolean(EXTRA_USE_SIDE_CHANNEL); 840 } 841 842 /** 843 * Gets the current notification interruption filter. 844 * <p> 845 * The interruption filter defines which notifications are allowed to 846 * interrupt the user (e.g. via sound & vibration) and is applied 847 * globally. 848 */ getCurrentInterruptionFilter()849 public @InterruptionFilter int getCurrentInterruptionFilter() { 850 if (Build.VERSION.SDK_INT < 23) { 851 // Prior to API 23, Interruption Filters were not implemented, so we return 852 // unknown filter level. 853 return INTERRUPTION_FILTER_UNKNOWN; 854 } 855 return Api23Impl.getCurrentInterruptionFilter(mNotificationManager); 856 } 857 858 /** 859 * Push a notification task for distribution to notification side channels. 860 */ pushSideChannelQueue(Task task)861 private void pushSideChannelQueue(Task task) { 862 synchronized (sLock) { 863 if (sSideChannelManager == null) { 864 sSideChannelManager = new SideChannelManager(mContext.getApplicationContext()); 865 } 866 sSideChannelManager.queueTask(task); 867 } 868 } 869 870 /** 871 * Helper class to manage a queue of pending tasks to send to notification side channel 872 * listeners. 873 */ 874 private static class SideChannelManager implements Handler.Callback, ServiceConnection { 875 private static final int MSG_QUEUE_TASK = 0; 876 private static final int MSG_SERVICE_CONNECTED = 1; 877 private static final int MSG_SERVICE_DISCONNECTED = 2; 878 private static final int MSG_RETRY_LISTENER_QUEUE = 3; 879 880 private final Context mContext; 881 private final HandlerThread mHandlerThread; 882 private final Handler mHandler; 883 private final Map<ComponentName, ListenerRecord> mRecordMap = 884 new HashMap<ComponentName, ListenerRecord>(); 885 private Set<String> mCachedEnabledPackages = new HashSet<String>(); 886 SideChannelManager(Context context)887 SideChannelManager(Context context) { 888 mContext = context; 889 mHandlerThread = new HandlerThread("NotificationManagerCompat"); 890 mHandlerThread.start(); 891 mHandler = new Handler(mHandlerThread.getLooper(), this); 892 } 893 894 /** 895 * Queue a new task to be sent to all listeners. This function can be called 896 * from any thread. 897 */ queueTask(Task task)898 public void queueTask(Task task) { 899 mHandler.obtainMessage(MSG_QUEUE_TASK, task).sendToTarget(); 900 } 901 902 @Override handleMessage(Message msg)903 public boolean handleMessage(Message msg) { 904 switch (msg.what) { 905 case MSG_QUEUE_TASK: 906 handleQueueTask((Task) msg.obj); 907 return true; 908 case MSG_SERVICE_CONNECTED: 909 ServiceConnectedEvent event = (ServiceConnectedEvent) msg.obj; 910 handleServiceConnected(event.componentName, event.iBinder); 911 return true; 912 case MSG_SERVICE_DISCONNECTED: 913 handleServiceDisconnected((ComponentName) msg.obj); 914 return true; 915 case MSG_RETRY_LISTENER_QUEUE: 916 handleRetryListenerQueue((ComponentName) msg.obj); 917 return true; 918 } 919 return false; 920 } 921 handleQueueTask(Task task)922 private void handleQueueTask(Task task) { 923 updateListenerMap(); 924 for (ListenerRecord record : mRecordMap.values()) { 925 record.taskQueue.add(task); 926 processListenerQueue(record); 927 } 928 } 929 handleServiceConnected(ComponentName componentName, IBinder iBinder)930 private void handleServiceConnected(ComponentName componentName, IBinder iBinder) { 931 ListenerRecord record = mRecordMap.get(componentName); 932 if (record != null) { 933 record.service = INotificationSideChannel.Stub.asInterface(iBinder); 934 record.retryCount = 0; 935 processListenerQueue(record); 936 } 937 } 938 handleServiceDisconnected(ComponentName componentName)939 private void handleServiceDisconnected(ComponentName componentName) { 940 ListenerRecord record = mRecordMap.get(componentName); 941 if (record != null) { 942 ensureServiceUnbound(record); 943 } 944 } 945 handleRetryListenerQueue(ComponentName componentName)946 private void handleRetryListenerQueue(ComponentName componentName) { 947 ListenerRecord record = mRecordMap.get(componentName); 948 if (record != null) { 949 processListenerQueue(record); 950 } 951 } 952 953 @Override onServiceConnected(ComponentName componentName, IBinder iBinder)954 public void onServiceConnected(ComponentName componentName, IBinder iBinder) { 955 if (Log.isLoggable(TAG, Log.DEBUG)) { 956 Log.d(TAG, "Connected to service " + componentName); 957 } 958 mHandler.obtainMessage(MSG_SERVICE_CONNECTED, 959 new ServiceConnectedEvent(componentName, iBinder)) 960 .sendToTarget(); 961 } 962 963 @Override onServiceDisconnected(ComponentName componentName)964 public void onServiceDisconnected(ComponentName componentName) { 965 if (Log.isLoggable(TAG, Log.DEBUG)) { 966 Log.d(TAG, "Disconnected from service " + componentName); 967 } 968 mHandler.obtainMessage(MSG_SERVICE_DISCONNECTED, componentName).sendToTarget(); 969 } 970 971 /** 972 * Check the current list of enabled listener packages and update the records map 973 * accordingly. 974 */ 975 @SuppressWarnings("deprecation") updateListenerMap()976 private void updateListenerMap() { 977 Set<String> enabledPackages = getEnabledListenerPackages(mContext); 978 if (enabledPackages.equals(mCachedEnabledPackages)) { 979 // Short-circuit when the list of enabled packages has not changed. 980 return; 981 } 982 mCachedEnabledPackages = enabledPackages; 983 List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServices( 984 new Intent().setAction(ACTION_BIND_SIDE_CHANNEL), 0); 985 Set<ComponentName> enabledComponents = new HashSet<ComponentName>(); 986 for (ResolveInfo resolveInfo : resolveInfos) { 987 if (!enabledPackages.contains(resolveInfo.serviceInfo.packageName)) { 988 continue; 989 } 990 ComponentName componentName = new ComponentName( 991 resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name); 992 if (resolveInfo.serviceInfo.permission != null) { 993 Log.w(TAG, "Permission present on component " + componentName 994 + ", not adding listener record."); 995 continue; 996 } 997 enabledComponents.add(componentName); 998 } 999 // Ensure all enabled components have a record in the listener map. 1000 for (ComponentName componentName : enabledComponents) { 1001 if (!mRecordMap.containsKey(componentName)) { 1002 if (Log.isLoggable(TAG, Log.DEBUG)) { 1003 Log.d(TAG, "Adding listener record for " + componentName); 1004 } 1005 mRecordMap.put(componentName, new ListenerRecord(componentName)); 1006 } 1007 } 1008 // Remove listener records that are no longer for enabled components. 1009 Iterator<Map.Entry<ComponentName, ListenerRecord>> it = 1010 mRecordMap.entrySet().iterator(); 1011 while (it.hasNext()) { 1012 Map.Entry<ComponentName, ListenerRecord> entry = it.next(); 1013 if (!enabledComponents.contains(entry.getKey())) { 1014 if (Log.isLoggable(TAG, Log.DEBUG)) { 1015 Log.d(TAG, "Removing listener record for " + entry.getKey()); 1016 } 1017 ensureServiceUnbound(entry.getValue()); 1018 it.remove(); 1019 } 1020 } 1021 } 1022 1023 /** 1024 * Ensure we are already attempting to bind to a service, or start a new binding if not. 1025 * 1026 * @return Whether the service bind attempt was successful. 1027 */ ensureServiceBound(ListenerRecord record)1028 private boolean ensureServiceBound(ListenerRecord record) { 1029 if (record.bound) { 1030 return true; 1031 } 1032 Intent intent = new Intent(ACTION_BIND_SIDE_CHANNEL).setComponent(record.componentName); 1033 record.bound = mContext.bindService(intent, this, Service.BIND_AUTO_CREATE 1034 | Service.BIND_WAIVE_PRIORITY); 1035 if (record.bound) { 1036 record.retryCount = 0; 1037 } else { 1038 Log.w(TAG, "Unable to bind to listener " + record.componentName); 1039 mContext.unbindService(this); 1040 } 1041 return record.bound; 1042 } 1043 1044 /** 1045 * Ensure we have unbound from a service. 1046 */ ensureServiceUnbound(ListenerRecord record)1047 private void ensureServiceUnbound(ListenerRecord record) { 1048 if (record.bound) { 1049 mContext.unbindService(this); 1050 record.bound = false; 1051 } 1052 record.service = null; 1053 } 1054 1055 /** 1056 * Schedule a delayed retry to communicate with a listener service. 1057 * After a maximum number of attempts (with exponential back-off), start 1058 * dropping pending tasks for this listener. 1059 */ scheduleListenerRetry(ListenerRecord record)1060 private void scheduleListenerRetry(ListenerRecord record) { 1061 if (mHandler.hasMessages(MSG_RETRY_LISTENER_QUEUE, record.componentName)) { 1062 return; 1063 } 1064 record.retryCount++; 1065 if (record.retryCount > SIDE_CHANNEL_RETRY_MAX_COUNT) { 1066 Log.w(TAG, "Giving up on delivering " + record.taskQueue.size() + " tasks to " 1067 + record.componentName + " after " + record.retryCount + " retries"); 1068 record.taskQueue.clear(); 1069 return; 1070 } 1071 int delayMs = SIDE_CHANNEL_RETRY_BASE_INTERVAL_MS * (1 << (record.retryCount - 1)); 1072 if (Log.isLoggable(TAG, Log.DEBUG)) { 1073 Log.d(TAG, "Scheduling retry for " + delayMs + " ms"); 1074 } 1075 Message msg = mHandler.obtainMessage(MSG_RETRY_LISTENER_QUEUE, record.componentName); 1076 mHandler.sendMessageDelayed(msg, delayMs); 1077 } 1078 1079 /** 1080 * Perform a processing step for a listener. First check the bind state, then attempt 1081 * to flush the task queue, and if an error is encountered, schedule a retry. 1082 */ processListenerQueue(ListenerRecord record)1083 private void processListenerQueue(ListenerRecord record) { 1084 if (Log.isLoggable(TAG, Log.DEBUG)) { 1085 Log.d(TAG, "Processing component " + record.componentName + ", " 1086 + record.taskQueue.size() + " queued tasks"); 1087 } 1088 if (record.taskQueue.isEmpty()) { 1089 return; 1090 } 1091 if (!ensureServiceBound(record) || record.service == null) { 1092 // Ensure bind has started and that a service interface is ready to use. 1093 scheduleListenerRetry(record); 1094 return; 1095 } 1096 // Attempt to flush all items in the task queue. 1097 while (true) { 1098 Task task = record.taskQueue.peek(); 1099 if (task == null) { 1100 break; 1101 } 1102 try { 1103 if (Log.isLoggable(TAG, Log.DEBUG)) { 1104 Log.d(TAG, "Sending task " + task); 1105 } 1106 task.send(record.service); 1107 record.taskQueue.remove(); 1108 } catch (DeadObjectException e) { 1109 if (Log.isLoggable(TAG, Log.DEBUG)) { 1110 Log.d(TAG, "Remote service has died: " + record.componentName); 1111 } 1112 break; 1113 } catch (RemoteException e) { 1114 Log.w(TAG, "RemoteException communicating with " + record.componentName, e); 1115 break; 1116 } 1117 } 1118 if (!record.taskQueue.isEmpty()) { 1119 // Some tasks were not sent, meaning an error was encountered, schedule a retry. 1120 scheduleListenerRetry(record); 1121 } 1122 } 1123 1124 /** A per-side-channel-service listener state record */ 1125 private static class ListenerRecord { 1126 final ComponentName componentName; 1127 /** Whether the service is currently bound to. */ 1128 boolean bound = false; 1129 /** The service stub provided by onServiceConnected */ 1130 INotificationSideChannel service; 1131 /** Queue of pending tasks to send to this listener service */ 1132 ArrayDeque<Task> taskQueue = new ArrayDeque<>(); 1133 /** Number of retries attempted while connecting to this listener service */ 1134 int retryCount = 0; 1135 ListenerRecord(ComponentName componentName)1136 ListenerRecord(ComponentName componentName) { 1137 this.componentName = componentName; 1138 } 1139 } 1140 } 1141 1142 private static class ServiceConnectedEvent { 1143 final ComponentName componentName; 1144 final IBinder iBinder; 1145 ServiceConnectedEvent(ComponentName componentName, final IBinder iBinder)1146 ServiceConnectedEvent(ComponentName componentName, 1147 final IBinder iBinder) { 1148 this.componentName = componentName; 1149 this.iBinder = iBinder; 1150 } 1151 } 1152 1153 private interface Task { send(INotificationSideChannel service)1154 void send(INotificationSideChannel service) throws RemoteException; 1155 } 1156 1157 private static class NotifyTask implements Task { 1158 final String packageName; 1159 final int id; 1160 final String tag; 1161 final Notification notif; 1162 NotifyTask(String packageName, int id, String tag, Notification notif)1163 NotifyTask(String packageName, int id, String tag, Notification notif) { 1164 this.packageName = packageName; 1165 this.id = id; 1166 this.tag = tag; 1167 this.notif = notif; 1168 } 1169 1170 @Override send(INotificationSideChannel service)1171 public void send(INotificationSideChannel service) throws RemoteException { 1172 service.notify(packageName, id, tag, notif); 1173 } 1174 1175 @Override toString()1176 public @NonNull String toString() { 1177 StringBuilder sb = new StringBuilder("NotifyTask["); 1178 sb.append("packageName:").append(packageName); 1179 sb.append(", id:").append(id); 1180 sb.append(", tag:").append(tag); 1181 sb.append("]"); 1182 return sb.toString(); 1183 } 1184 } 1185 1186 private static class CancelTask implements Task { 1187 final String packageName; 1188 final int id; 1189 final String tag; 1190 final boolean all; 1191 CancelTask(String packageName)1192 CancelTask(String packageName) { 1193 this.packageName = packageName; 1194 this.id = 0; 1195 this.tag = null; 1196 this.all = true; 1197 } 1198 CancelTask(String packageName, int id, String tag)1199 CancelTask(String packageName, int id, String tag) { 1200 this.packageName = packageName; 1201 this.id = id; 1202 this.tag = tag; 1203 this.all = false; 1204 } 1205 1206 @Override send(INotificationSideChannel service)1207 public void send(INotificationSideChannel service) throws RemoteException { 1208 if (all) { 1209 service.cancelAll(packageName); 1210 } else { 1211 service.cancel(packageName, id, tag); 1212 } 1213 } 1214 1215 @Override toString()1216 public @NonNull String toString() { 1217 StringBuilder sb = new StringBuilder("CancelTask["); 1218 sb.append("packageName:").append(packageName); 1219 sb.append(", id:").append(id); 1220 sb.append(", tag:").append(tag); 1221 sb.append(", all:").append(all); 1222 sb.append("]"); 1223 return sb.toString(); 1224 } 1225 } 1226 1227 /** 1228 * A class for wrapping calls to {@link NotificationManager} methods which 1229 * were added in API 23; these calls must be wrapped to avoid performance issues. 1230 * See the UnsafeNewApiCall lint rule for more details. 1231 */ 1232 @RequiresApi(23) 1233 static class Api23Impl { Api23Impl()1234 private Api23Impl() { } 1235 getActiveNotifications( NotificationManager notificationManager)1236 static List<StatusBarNotification> getActiveNotifications( 1237 NotificationManager notificationManager) { 1238 StatusBarNotification[] notifs = notificationManager.getActiveNotifications(); 1239 if (notifs == null) { 1240 return new ArrayList<>(); 1241 } 1242 return Arrays.asList(notifs); 1243 } 1244 getCurrentInterruptionFilter( NotificationManager notificationManager)1245 static int getCurrentInterruptionFilter( 1246 NotificationManager notificationManager) { 1247 return notificationManager.getCurrentInterruptionFilter(); 1248 } 1249 } 1250 1251 /** 1252 * A class for wrapping calls to {@link NotificationManager} methods which 1253 * were added in API 24; these calls must be wrapped to avoid performance issues. 1254 * See the UnsafeNewApiCall lint rule for more details. 1255 */ 1256 @RequiresApi(24) 1257 static class Api24Impl { Api24Impl()1258 private Api24Impl() { } 1259 areNotificationsEnabled(NotificationManager notificationManager)1260 static boolean areNotificationsEnabled(NotificationManager notificationManager) { 1261 return notificationManager.areNotificationsEnabled(); 1262 } 1263 getImportance(NotificationManager notificationManager)1264 static int getImportance(NotificationManager notificationManager) { 1265 return notificationManager.getImportance(); 1266 } 1267 } 1268 1269 /** 1270 * A class for wrapping calls to {@link Notification.Builder} methods which 1271 * were added in API 26; these calls must be wrapped to avoid performance issues. 1272 * See the UnsafeNewApiCall lint rule for more details. 1273 */ 1274 @RequiresApi(26) 1275 static class Api26Impl { Api26Impl()1276 private Api26Impl() { 1277 // This class is not instantiable. 1278 } 1279 createNotificationChannel(NotificationManager notificationManager, NotificationChannel channel)1280 static void createNotificationChannel(NotificationManager notificationManager, 1281 NotificationChannel channel) { 1282 notificationManager.createNotificationChannel(channel); 1283 } 1284 getNotificationChannel(NotificationManager notificationManager, String channelId)1285 static NotificationChannel getNotificationChannel(NotificationManager notificationManager, 1286 String channelId) { 1287 return notificationManager.getNotificationChannel(channelId); 1288 } 1289 createNotificationChannels( NotificationManager notificationManager, List<NotificationChannel> channels)1290 static void createNotificationChannels( 1291 NotificationManager notificationManager, List<NotificationChannel> channels) { 1292 notificationManager.createNotificationChannels(channels); 1293 } 1294 getNotificationChannels( NotificationManager notificationManager)1295 static List<NotificationChannel> getNotificationChannels( 1296 NotificationManager notificationManager) { 1297 return notificationManager.getNotificationChannels(); 1298 } 1299 createNotificationChannelGroup(NotificationManager notificationManager, NotificationChannelGroup group)1300 static void createNotificationChannelGroup(NotificationManager notificationManager, 1301 NotificationChannelGroup group) { 1302 notificationManager.createNotificationChannelGroup(group); 1303 } 1304 createNotificationChannelGroups(NotificationManager notificationManager, List<NotificationChannelGroup> groups)1305 static void createNotificationChannelGroups(NotificationManager notificationManager, 1306 List<NotificationChannelGroup> groups) { 1307 notificationManager.createNotificationChannelGroups(groups); 1308 } 1309 getNotificationChannelGroups( NotificationManager notificationManager)1310 static List<NotificationChannelGroup> getNotificationChannelGroups( 1311 NotificationManager notificationManager) { 1312 return notificationManager.getNotificationChannelGroups(); 1313 } 1314 deleteNotificationChannel(NotificationManager notificationManager, String channelId)1315 static void deleteNotificationChannel(NotificationManager notificationManager, 1316 String channelId) { 1317 notificationManager.deleteNotificationChannel(channelId); 1318 } 1319 deleteNotificationChannelGroup(NotificationManager notificationManager, String groupId)1320 static void deleteNotificationChannelGroup(NotificationManager notificationManager, 1321 String groupId) { 1322 notificationManager.deleteNotificationChannelGroup(groupId); 1323 } 1324 1325 getId(NotificationChannel notificationChannel)1326 static String getId(NotificationChannel notificationChannel) { 1327 return notificationChannel.getId(); 1328 } 1329 getId(NotificationChannelGroup notificationChannelGroup)1330 static String getId(NotificationChannelGroup notificationChannelGroup) { 1331 return notificationChannelGroup.getId(); 1332 } 1333 } 1334 1335 /** 1336 * A class for wrapping calls to {@link Notification.Builder} methods which 1337 * were added in API 28; these calls must be wrapped to avoid performance issues. 1338 * See the UnsafeNewApiCall lint rule for more details. 1339 */ 1340 @RequiresApi(28) 1341 static class Api28Impl { Api28Impl()1342 private Api28Impl() { } 1343 getNotificationChannelGroup( NotificationManager notificationManager, String channelGroupId)1344 static NotificationChannelGroup getNotificationChannelGroup( 1345 NotificationManager notificationManager, String channelGroupId) { 1346 return notificationManager.getNotificationChannelGroup(channelGroupId); 1347 } 1348 } 1349 1350 /** 1351 * A class for wrapping calls to {@link Notification.Builder} methods which 1352 * were added in API 30; these calls must be wrapped to avoid performance issues. 1353 * See the UnsafeNewApiCall lint rule for more details. 1354 */ 1355 @RequiresApi(30) 1356 static class Api30Impl { Api30Impl()1357 private Api30Impl() { } 1358 getParentChannelId(NotificationChannel notificationChannel)1359 static String getParentChannelId(NotificationChannel notificationChannel) { 1360 return notificationChannel.getParentChannelId(); 1361 } 1362 getNotificationChannel(NotificationManager notificationManager, String channelId, String conversationId)1363 static NotificationChannel getNotificationChannel(NotificationManager notificationManager, 1364 String channelId, String conversationId) { 1365 return notificationManager.getNotificationChannel(channelId, conversationId); 1366 } 1367 } 1368 1369 /** 1370 * A class for wrapping calls to {@link Notification.Builder} methods which 1371 * were added in API 34; these calls must be wrapped to avoid performance issues. 1372 * See the UnsafeNewApiCall lint rule for more details. 1373 */ 1374 @RequiresApi(34) 1375 static class Api34Impl { Api34Impl()1376 private Api34Impl() { } 1377 canUseFullScreenIntent(NotificationManager notificationManager)1378 static boolean canUseFullScreenIntent(NotificationManager notificationManager) { 1379 return notificationManager.canUseFullScreenIntent(); 1380 } 1381 } 1382 } 1383