1 /** 2 * Copyright (c) 2018, 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 com.android.server.notification; 18 19 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; 20 import static android.app.Flags.notificationClassificationUi; 21 import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID; 22 import static android.app.NotificationChannel.NEWS_ID; 23 import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID; 24 import static android.app.NotificationChannel.PROMOTIONS_ID; 25 import static android.app.NotificationChannel.RECS_ID; 26 import static android.app.NotificationChannel.SOCIAL_MEDIA_ID; 27 import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS; 28 import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE; 29 import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; 30 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; 31 import static android.app.NotificationManager.IMPORTANCE_DEFAULT; 32 import static android.app.NotificationManager.IMPORTANCE_LOW; 33 import static android.app.NotificationManager.IMPORTANCE_MAX; 34 import static android.app.NotificationManager.IMPORTANCE_NONE; 35 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; 36 import static android.os.UserHandle.USER_SYSTEM; 37 import static android.service.notification.Flags.notificationClassification; 38 39 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; 40 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; 41 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES; 42 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED; 43 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED; 44 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED; 45 import static com.android.server.notification.PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE; 46 import static com.android.server.notification.PreferencesHelper.LockableAppFields.USER_LOCKED_PROMOTABLE; 47 48 import android.annotation.FlaggedApi; 49 import android.annotation.IntDef; 50 import android.annotation.NonNull; 51 import android.annotation.Nullable; 52 import android.annotation.UserIdInt; 53 import android.app.AppOpsManager; 54 import android.app.Notification; 55 import android.app.NotificationChannel; 56 import android.app.NotificationChannelGroup; 57 import android.app.NotificationManager; 58 import android.app.ZenBypassingApp; 59 import android.content.AttributionSource; 60 import android.content.Context; 61 import android.content.pm.ApplicationInfo; 62 import android.content.pm.PackageInfo; 63 import android.content.pm.PackageManager; 64 import android.content.pm.ParceledListSlice; 65 import android.content.pm.UserInfo; 66 import android.metrics.LogMaker; 67 import android.net.Uri; 68 import android.os.Build; 69 import android.os.Process; 70 import android.os.UserHandle; 71 import android.permission.PermissionManager; 72 import android.provider.Settings; 73 import android.service.notification.Adjustment; 74 import android.service.notification.ConversationChannelWrapper; 75 import android.service.notification.NotificationListenerService; 76 import android.service.notification.RankingHelperProto; 77 import android.service.notification.ZenModeConfig; 78 import android.text.TextUtils; 79 import android.text.format.DateUtils; 80 import android.util.ArrayMap; 81 import android.util.ArraySet; 82 import android.util.IntArray; 83 import android.util.Pair; 84 import android.util.Slog; 85 import android.util.SparseBooleanArray; 86 import android.util.StatsEvent; 87 import android.util.proto.ProtoOutputStream; 88 89 import androidx.annotation.VisibleForTesting; 90 91 import com.android.internal.R; 92 import com.android.internal.annotations.GuardedBy; 93 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; 94 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags; 95 import com.android.internal.logging.MetricsLogger; 96 import com.android.internal.notification.NotificationChannelGroupsHelper; 97 import com.android.internal.util.FrameworkStatsLog; 98 import com.android.internal.util.Preconditions; 99 import com.android.internal.util.XmlUtils; 100 import com.android.modules.utils.TypedXmlPullParser; 101 import com.android.modules.utils.TypedXmlSerializer; 102 import com.android.server.notification.PermissionHelper.PackagePermission; 103 104 import org.json.JSONArray; 105 import org.json.JSONException; 106 import org.json.JSONObject; 107 import org.xmlpull.v1.XmlPullParser; 108 import org.xmlpull.v1.XmlPullParserException; 109 110 import java.io.IOException; 111 import java.io.PrintWriter; 112 import java.time.Clock; 113 import java.time.Duration; 114 import java.util.ArrayList; 115 import java.util.Arrays; 116 import java.util.Collection; 117 import java.util.List; 118 import java.util.Map; 119 import java.util.Objects; 120 import java.util.Set; 121 import java.util.concurrent.ConcurrentHashMap; 122 123 public class PreferencesHelper implements RankingConfig { 124 private static final String TAG = "NotificationPrefHelper"; 125 private final int XML_VERSION; 126 /** What version to check to do the upgrade for bubbles. */ 127 private static final int XML_VERSION_BUBBLES_UPGRADE = 1; 128 /** The first xml version with notification permissions enabled. */ 129 private static final int XML_VERSION_NOTIF_PERMISSION = 3; 130 /** The first xml version that notifies users to review their notification permissions */ 131 private static final int XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION = 4; 132 @VisibleForTesting 133 static final int UNKNOWN_UID = UserHandle.USER_NULL; 134 // The amount of time pacakage preferences can exist without the app being installed. 135 private static final long PREF_GRACE_PERIOD_MS = Duration.ofDays(2).toMillis(); 136 137 @VisibleForTesting 138 static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000; 139 @VisibleForTesting 140 static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 6000; 141 142 private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000; 143 private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000; 144 private static final int NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT = 1000; 145 private static final int NOTIFICATION_CHANNEL_DELETION_RETENTION_DAYS = 30; 146 147 @VisibleForTesting 148 static final String TAG_RANKING = "ranking"; 149 private static final String TAG_PACKAGE = "package"; 150 private static final String TAG_CHANNEL = "channel"; 151 private static final String TAG_GROUP = "channelGroup"; 152 private static final String TAG_DELEGATE = "delegate"; 153 private static final String TAG_STATUS_ICONS = "silent_status_icons"; 154 155 private static final String ATT_VERSION = "version"; 156 private static final String ATT_NAME = "name"; 157 private static final String ATT_UID = "uid"; 158 private static final String ATT_LAST_BUBBLES_VERSION_UPGRADE = "last_bubbles_version_upgrade"; 159 160 private static final String ATT_USERID = "userid"; 161 private static final String ATT_ID = "id"; 162 private static final String ATT_ALLOW_BUBBLE = "allow_bubble"; 163 private static final String ATT_PRIORITY = "priority"; 164 private static final String ATT_VISIBILITY = "visibility"; 165 private static final String ATT_IMPORTANCE = "importance"; 166 private static final String ATT_SHOW_BADGE = "show_badge"; 167 private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields"; 168 private static final String ATT_ENABLED = "enabled"; 169 private static final String ATT_HIDE_SILENT = "hide_gentle"; 170 private static final String ATT_SENT_INVALID_MESSAGE = "sent_invalid_msg"; 171 private static final String ATT_SENT_VALID_MESSAGE = "sent_valid_msg"; 172 private static final String ATT_USER_DEMOTED_INVALID_MSG_APP = "user_demote_msg_app"; 173 private static final String ATT_SENT_VALID_BUBBLE = "sent_valid_bubble"; 174 private static final String ATT_PROMOTE_NOTIFS = "promote"; 175 176 private static final String ATT_CREATION_TIME = "creation_time"; 177 178 private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT; 179 private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE; 180 private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED; 181 @VisibleForTesting 182 static final boolean DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS = false; 183 private static final boolean DEFAULT_SHOW_BADGE = true; 184 185 private static final boolean DEFAULT_APP_LOCKED_IMPORTANCE = false; 186 private static final boolean DEFAULT_CAN_HAVE_PROMOTED_NOTIFS = true; 187 188 static final boolean DEFAULT_BUBBLES_ENABLED = true; 189 @VisibleForTesting 190 static final int DEFAULT_BUBBLE_PREFERENCE = BUBBLE_PREFERENCE_NONE; 191 192 private static final int NOTIFICATION_UPDATE_LOG_SUBTYPE_FROM_APP = 0; 193 private static final int NOTIFICATION_UPDATE_LOG_SUBTYPE_FROM_USER = 1; 194 195 /** 196 * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable 197 * fields. 198 */ 199 private static final int DEFAULT_LOCKED_APP_FIELDS = 0; 200 201 /** 202 * All user-lockable fields for a given application. 203 */ 204 @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE, 205 LockableAppFields.USER_LOCKED_BUBBLE, 206 LockableAppFields.USER_LOCKED_PROMOTABLE}) 207 public @interface LockableAppFields { 208 int USER_LOCKED_IMPORTANCE = 0x00000001; 209 int USER_LOCKED_BUBBLE = 0x00000002; 210 int USER_LOCKED_PROMOTABLE = 0x00000004; 211 } 212 213 private final Object mLock = new Object(); 214 // pkg|uid => PackagePreferences 215 @GuardedBy("mLock") 216 private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>(); 217 // pkg|userId => PackagePreferences 218 @GuardedBy("mLock") 219 private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>(); 220 221 private final Context mContext; 222 private final PackageManager mPm; 223 private final RankingHandler mRankingHandler; 224 private final ZenModeHelper mZenModeHelper; 225 private final PermissionHelper mPermissionHelper; 226 private final PermissionManager mPermissionManager; 227 private final NotificationChannelLogger mNotificationChannelLogger; 228 private final AppOpsManager mAppOps; 229 private final ManagedServices.UserProfiles mUserProfiles; 230 231 private SparseBooleanArray mBadgingEnabled; 232 private SparseBooleanArray mBubblesEnabled; 233 private SparseBooleanArray mLockScreenShowNotifications; 234 private SparseBooleanArray mLockScreenPrivateNotifications; 235 private boolean mIsMediaNotificationFilteringEnabled; 236 // Whether the current user has any channels marked as "priority channels" -- but not 237 // necessarily whether they are permitted to bypass DND by current zen policy. 238 private boolean mCurrentUserHasPriorityChannels; 239 private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS; 240 private final boolean mShowReviewPermissionsNotification; 241 242 Clock mClock; 243 PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, ZenModeHelper zenHelper, PermissionHelper permHelper, PermissionManager permManager, NotificationChannelLogger notificationChannelLogger, AppOpsManager appOpsManager, ManagedServices.UserProfiles userProfiles, boolean showReviewPermissionsNotification, Clock clock)244 public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, 245 ZenModeHelper zenHelper, PermissionHelper permHelper, PermissionManager permManager, 246 NotificationChannelLogger notificationChannelLogger, 247 AppOpsManager appOpsManager, ManagedServices.UserProfiles userProfiles, 248 boolean showReviewPermissionsNotification, Clock clock) { 249 mContext = context; 250 mZenModeHelper = zenHelper; 251 mRankingHandler = rankingHandler; 252 mPermissionHelper = permHelper; 253 mPermissionManager = permManager; 254 mPm = pm; 255 mNotificationChannelLogger = notificationChannelLogger; 256 mAppOps = appOpsManager; 257 mUserProfiles = userProfiles; 258 mShowReviewPermissionsNotification = showReviewPermissionsNotification; 259 mIsMediaNotificationFilteringEnabled = context.getResources() 260 .getBoolean(R.bool.config_quickSettingsShowMediaPlayer); 261 mClock = clock; 262 XML_VERSION = 4; 263 264 updateBadgingEnabled(); 265 updateBubblesEnabled(); 266 updateMediaNotificationFilteringEnabled(); 267 } 268 onBootPhaseAppsCanStart()269 void onBootPhaseAppsCanStart() { 270 // IpcDataCaches must be invalidated once data becomes available, as queries will only 271 // begin to be cached after the first invalidation signal. At this point, we know about all 272 // notification channels. 273 if (android.app.Flags.nmBinderPerfCacheChannels()) { 274 invalidateNotificationChannelCache(); 275 invalidateNotificationChannelGroupCache(); 276 } 277 } 278 readXml(TypedXmlPullParser parser, boolean forRestore, int userId)279 public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId) 280 throws XmlPullParserException, IOException { 281 int type = parser.getEventType(); 282 if (type != XmlPullParser.START_TAG) return; 283 String tag = parser.getName(); 284 if (!TAG_RANKING.equals(tag)) return; 285 286 final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1); 287 boolean upgradeForBubbles = parser.getAttributeInt(null, 288 ATT_LAST_BUBBLES_VERSION_UPGRADE, -1) < Build.VERSION.SDK_INT; 289 boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION); 290 if (mShowReviewPermissionsNotification 291 && (xmlVersion < XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION)) { 292 // make a note that we should show the notification at some point. 293 // it shouldn't be possible for the user to already have seen it, as the XML version 294 // would be newer then. 295 Settings.Global.putInt(mContext.getContentResolver(), 296 Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE, 297 NotificationManagerService.REVIEW_NOTIF_STATE_SHOULD_SHOW); 298 } 299 synchronized (mLock) { 300 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 301 tag = parser.getName(); 302 if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) { 303 break; 304 } 305 if (type == XmlPullParser.START_TAG) { 306 if (TAG_STATUS_ICONS.equals(tag)) { 307 if (forRestore && userId != USER_SYSTEM) { 308 continue; 309 } 310 mHideSilentStatusBarIcons = parser.getAttributeBoolean(null, 311 ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS); 312 } else if (TAG_PACKAGE.equals(tag)) { 313 String name = parser.getAttributeValue(null, ATT_NAME); 314 if (!TextUtils.isEmpty(name)) { 315 restorePackage(parser, forRestore, userId, name, upgradeForBubbles, 316 migrateToPermission); 317 } 318 } 319 } 320 } 321 } 322 } 323 324 @GuardedBy("mPackagePreferences") 325 private void restorePackage(TypedXmlPullParser parser, boolean forRestore, 326 @UserIdInt int userId, String name, boolean upgradeForBubbles, 327 boolean migrateToPermission) { 328 try { 329 int uid = parser.getAttributeInt(null, ATT_UID, UNKNOWN_UID); 330 if (forRestore) { 331 try { 332 uid = mPm.getPackageUidAsUser(name, userId); 333 } catch (PackageManager.NameNotFoundException e) { 334 // noop 335 } 336 } 337 boolean skipWarningLogged = false; 338 boolean skipGroupWarningLogged = false; 339 int bubblePref = parser.getAttributeInt(null, ATT_ALLOW_BUBBLE, 340 DEFAULT_BUBBLE_PREFERENCE); 341 boolean bubbleLocked = (parser.getAttributeInt(null, 342 ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS) & USER_LOCKED_BUBBLE) 343 != 0; 344 if (!bubbleLocked 345 && upgradeForBubbles 346 && uid != UNKNOWN_UID 347 && mAppOps.noteOpNoThrow(OP_SYSTEM_ALERT_WINDOW, uid, name, null, 348 "check-notif-bubble") == AppOpsManager.MODE_ALLOWED) { 349 // User hasn't changed bubble pref & the app has SAW, so allow all bubbles. 350 bubblePref = BUBBLE_PREFERENCE_ALL; 351 } 352 int appImportance = parser.getAttributeInt(null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); 353 354 // when data is loaded from disk it's loaded as USER_ALL, but restored data that 355 // is pending app install needs the user id that the data was restored to 356 int fixedUserId = userId; 357 if (Flags.persistIncompleteRestoreData()) { 358 if (!forRestore && uid == UNKNOWN_UID) { 359 fixedUserId = parser.getAttributeInt(null, ATT_USERID, USER_SYSTEM); 360 } 361 } 362 PackagePreferences r = getOrCreatePackagePreferencesLocked( 363 name, fixedUserId, uid, 364 appImportance, 365 parser.getAttributeInt(null, ATT_PRIORITY, DEFAULT_PRIORITY), 366 parser.getAttributeInt(null, ATT_VISIBILITY, DEFAULT_VISIBILITY), 367 parser.getAttributeBoolean(null, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE), 368 bubblePref, parser.getAttributeLong(null, ATT_CREATION_TIME, mClock.millis())); 369 r.bubblePreference = bubblePref; 370 r.priority = parser.getAttributeInt(null, ATT_PRIORITY, DEFAULT_PRIORITY); 371 r.visibility = parser.getAttributeInt(null, ATT_VISIBILITY, DEFAULT_VISIBILITY); 372 r.showBadge = parser.getAttributeBoolean(null, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE); 373 r.lockedAppFields = parser.getAttributeInt(null, 374 ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS); 375 r.hasSentInvalidMessage = parser.getAttributeBoolean(null, ATT_SENT_INVALID_MESSAGE, 376 false); 377 r.hasSentValidMessage = parser.getAttributeBoolean(null, ATT_SENT_VALID_MESSAGE, false); 378 r.userDemotedMsgApp = parser.getAttributeBoolean( 379 null, ATT_USER_DEMOTED_INVALID_MSG_APP, false); 380 r.hasSentValidBubble = parser.getAttributeBoolean(null, ATT_SENT_VALID_BUBBLE, false); 381 if (android.app.Flags.uiRichOngoing()) { 382 r.canHavePromotedNotifs = parser.getAttributeBoolean(null, ATT_PROMOTE_NOTIFS, 383 DEFAULT_CAN_HAVE_PROMOTED_NOTIFS); 384 } 385 386 final int innerDepth = parser.getDepth(); 387 int type; 388 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 389 && (type != XmlPullParser.END_TAG 390 || parser.getDepth() > innerDepth)) { 391 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 392 continue; 393 } 394 395 String tagName = parser.getName(); 396 // Channel groups 397 if (TAG_GROUP.equals(tagName)) { 398 if (r.groups.size() >= NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT) { 399 if (!skipGroupWarningLogged) { 400 Slog.w(TAG, "Skipping further groups for " + r.pkg); 401 skipGroupWarningLogged = true; 402 } 403 continue; 404 } 405 String id = parser.getAttributeValue(null, ATT_ID); 406 CharSequence groupName = parser.getAttributeValue(null, ATT_NAME); 407 if (!TextUtils.isEmpty(id)) { 408 NotificationChannelGroup group = 409 new NotificationChannelGroup(id, groupName); 410 group.populateFromXml(parser); 411 r.groups.put(id, group); 412 } 413 } 414 // Channels 415 if (TAG_CHANNEL.equals(tagName)) { 416 if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) { 417 if (!skipWarningLogged) { 418 Slog.w(TAG, "Skipping further channels for " + r.pkg); 419 skipWarningLogged = true; 420 } 421 continue; 422 } 423 restoreChannel(parser, forRestore, r); 424 } 425 426 // Delegate 427 if (TAG_DELEGATE.equals(tagName)) { 428 int delegateId = parser.getAttributeInt(null, ATT_UID, UNKNOWN_UID); 429 String delegateName = XmlUtils.readStringAttribute(parser, ATT_NAME); 430 boolean delegateEnabled = parser.getAttributeBoolean( 431 null, ATT_ENABLED, Delegate.DEFAULT_ENABLED); 432 Delegate d = null; 433 if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(delegateName)) { 434 d = new Delegate(delegateName, delegateId, delegateEnabled); 435 } 436 r.delegate = d; 437 } 438 } 439 440 try { 441 deleteDefaultChannelIfNeededLocked(r); 442 } catch (PackageManager.NameNotFoundException e) { 443 Slog.e(TAG, "deleteDefaultChannelIfNeededLocked - Exception: " + e); 444 } 445 446 if (migrateToPermission) { 447 r.importance = appImportance; 448 r.migrateToPm = true; 449 } 450 } catch (Exception e) { 451 Slog.w(TAG, "Failed to restore pkg", e); 452 } 453 } 454 455 @GuardedBy("mPackagePreferences") 456 private void restoreChannel(TypedXmlPullParser parser, boolean forRestore, 457 PackagePreferences r) { 458 try { 459 String id = parser.getAttributeValue(null, ATT_ID); 460 if (!notificationClassification() && SYSTEM_RESERVED_IDS.contains(id)) { 461 // delete bundle channels if flag is rolled back 462 return; 463 } 464 String channelName = parser.getAttributeValue(null, ATT_NAME); 465 int channelImportance = parser.getAttributeInt( 466 null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); 467 if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) { 468 NotificationChannel channel = new NotificationChannel( 469 id, channelName, channelImportance); 470 if (forRestore) { 471 final boolean pkgInstalled = r.uid != UNKNOWN_UID; 472 channel.populateFromXmlForRestore(parser, pkgInstalled, mContext); 473 } else { 474 channel.populateFromXml(parser); 475 } 476 channel.setImportanceLockedByCriticalDeviceFunction( 477 r.defaultAppLockedImportance || r.fixedImportance); 478 479 if (notificationClassification()) { 480 if (SYSTEM_RESERVED_IDS.contains(id) && channel.isDeleted() ) { 481 channel.setDeleted(false); 482 } 483 } 484 485 if (isShortcutOk(channel) && isDeletionOk(channel)) { 486 r.channels.put(id, channel); 487 } 488 } 489 } catch (Exception e) { 490 Slog.w(TAG, "could not restore channel for " + r.pkg, e); 491 } 492 } 493 494 @GuardedBy("mPackagePreferences") 495 private boolean hasUserConfiguredSettings(PackagePreferences p){ 496 boolean hasChangedChannel = false; 497 for (NotificationChannel channel : p.channels.values()) { 498 if (channel.getUserLockedFields() != 0) { 499 hasChangedChannel = true; 500 break; 501 } 502 } 503 return hasChangedChannel || p.importance == IMPORTANCE_NONE; 504 } 505 506 private boolean isShortcutOk(NotificationChannel channel) { 507 boolean isInvalidShortcutChannel = 508 channel.getConversationId() != null && 509 channel.getConversationId().contains( 510 PLACEHOLDER_CONVERSATION_ID); 511 return !isInvalidShortcutChannel; 512 } 513 514 private boolean isDeletionOk(NotificationChannel nc) { 515 if (!nc.isDeleted()) { 516 return true; 517 } 518 long boundary = System.currentTimeMillis() - ( 519 DateUtils.DAY_IN_MILLIS * NOTIFICATION_CHANNEL_DELETION_RETENTION_DAYS); 520 if (nc.getDeletedTimeMs() <= boundary) { 521 return false; 522 } 523 return true; 524 } 525 526 private PackagePreferences getPackagePreferencesLocked(String pkg, int uid) { 527 final String key = packagePreferencesKey(pkg, uid); 528 return mPackagePreferences.get(key); 529 } 530 531 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, 532 int uid) { 533 // TODO (b/194833441): use permissionhelper instead of DEFAULT_IMPORTANCE 534 return getOrCreatePackagePreferencesLocked(pkg, UserHandle.getUserId(uid), uid, 535 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE, 536 DEFAULT_BUBBLE_PREFERENCE, mClock.millis()); 537 } 538 539 @GuardedBy("mLock") 540 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, 541 @UserIdInt int userId, int uid, int importance, int priority, int visibility, 542 boolean showBadge, int bubblePreference, long creationTime) { 543 boolean created = false; 544 final String key = packagePreferencesKey(pkg, uid); 545 PackagePreferences 546 r = (uid == UNKNOWN_UID) 547 ? mRestoredWithoutUids.get(unrestoredPackageKey(pkg, userId)) 548 : mPackagePreferences.get(key); 549 if (r == null) { 550 created = true; 551 r = new PackagePreferences(); 552 r.pkg = pkg; 553 r.uid = uid; 554 r.importance = importance; 555 r.priority = priority; 556 r.visibility = visibility; 557 r.showBadge = showBadge; 558 r.bubblePreference = bubblePreference; 559 if (Flags.persistIncompleteRestoreData()) { 560 if (r.uid == UNKNOWN_UID) { 561 r.creationTime = creationTime; 562 } 563 } 564 565 try { 566 createDefaultChannelIfNeededLocked(r); 567 } catch (PackageManager.NameNotFoundException e) { 568 Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e); 569 } 570 571 if (r.uid == UNKNOWN_UID) { 572 if (Flags.persistIncompleteRestoreData()) { 573 r.userIdWhenUidUnknown = userId; 574 } 575 mRestoredWithoutUids.put(unrestoredPackageKey(pkg, userId), r); 576 } else { 577 mPackagePreferences.put(key, r); 578 } 579 } 580 if (r.uid == UNKNOWN_UID) { 581 if (Flags.persistIncompleteRestoreData() 582 && PREF_GRACE_PERIOD_MS < (mClock.millis() - r.creationTime)) { 583 mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, userId)); 584 } 585 } 586 if (android.app.Flags.nmBinderPerfCacheChannels() && created) { 587 invalidateNotificationChannelCache(); 588 } 589 return r; 590 } 591 592 private boolean shouldHaveDefaultChannel(PackagePreferences r) throws 593 PackageManager.NameNotFoundException { 594 final int userId = UserHandle.getUserId(r.uid); 595 final ApplicationInfo applicationInfo = 596 mPm.getApplicationInfoAsUser(r.pkg, 0, userId); 597 if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) { 598 // O apps should not have the default channel. 599 return false; 600 } 601 602 // Otherwise, this app should have the default channel. 603 return true; 604 } 605 606 private boolean deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws 607 PackageManager.NameNotFoundException { 608 if (!r.channels.containsKey(DEFAULT_CHANNEL_ID)) { 609 // Not present 610 return false; 611 } 612 613 if (shouldHaveDefaultChannel(r)) { 614 // Keep the default channel until upgraded. 615 return false; 616 } 617 618 // Remove Default Channel. 619 r.channels.remove(DEFAULT_CHANNEL_ID); 620 621 return true; 622 } 623 624 private boolean createDefaultChannelIfNeededLocked(PackagePreferences r) throws 625 PackageManager.NameNotFoundException { 626 if (r.uid == UNKNOWN_UID) { 627 return false; 628 } 629 630 if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) { 631 r.channels.get(DEFAULT_CHANNEL_ID).setName(mContext.getString( 632 com.android.internal.R.string.default_notification_channel_label)); 633 return false; 634 } 635 636 if (!shouldHaveDefaultChannel(r)) { 637 // Keep the default channel until upgraded. 638 return false; 639 } 640 641 // Create Default Channel 642 NotificationChannel channel; 643 channel = new NotificationChannel( 644 DEFAULT_CHANNEL_ID, 645 mContext.getString(R.string.default_notification_channel_label), 646 r.importance); 647 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX); 648 channel.setLockscreenVisibility(r.visibility); 649 if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) { 650 channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); 651 } 652 if (r.priority != DEFAULT_PRIORITY) { 653 channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); 654 } 655 if (r.visibility != DEFAULT_VISIBILITY) { 656 channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY); 657 } 658 r.channels.put(channel.getId(), channel); 659 660 return true; 661 } 662 663 private NotificationChannel addReservedChannelLocked(PackagePreferences p, String channelId) { 664 String label = ""; 665 switch (channelId) { 666 case PROMOTIONS_ID: 667 label = mContext.getString(R.string.promotional_notification_channel_label); 668 break; 669 case RECS_ID: 670 label = mContext.getString(R.string.recs_notification_channel_label); 671 break; 672 case NEWS_ID: 673 label = mContext.getString(R.string.news_notification_channel_label); 674 break; 675 case SOCIAL_MEDIA_ID: 676 label = mContext.getString(R.string.social_notification_channel_label); 677 break; 678 } 679 NotificationChannel channel = new NotificationChannel(channelId, label, IMPORTANCE_LOW); 680 p.channels.put(channelId, channel); 681 if (android.app.Flags.nmBinderPerfCacheChannels()) { 682 invalidateNotificationChannelCache(); 683 } 684 return channel; 685 } 686 687 public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException { 688 out.startTag(null, TAG_RANKING); 689 out.attributeInt(null, ATT_VERSION, XML_VERSION); 690 out.attributeInt(null, ATT_LAST_BUBBLES_VERSION_UPGRADE, Build.VERSION.SDK_INT); 691 if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS) { 692 out.startTag(null, TAG_STATUS_ICONS); 693 out.attributeBoolean(null, ATT_HIDE_SILENT, mHideSilentStatusBarIcons); 694 out.endTag(null, TAG_STATUS_ICONS); 695 } 696 ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> notifPermissions = new ArrayMap<>(); 697 if (forBackup) { 698 notifPermissions = mPermissionHelper.getNotificationPermissionValues(userId); 699 } 700 701 synchronized (mLock) { 702 final int N = mPackagePreferences.size(); 703 for (int i = 0; i < N; i++) { 704 final PackagePreferences r = mPackagePreferences.valueAt(i); 705 if (forBackup && UserHandle.getUserId(r.uid) != userId) { 706 continue; 707 } 708 writePackageXml(r, out, notifPermissions, forBackup); 709 } 710 711 if (Flags.persistIncompleteRestoreData() && !forBackup) { 712 final int M = mRestoredWithoutUids.size(); 713 for (int i = 0; i < M; i++) { 714 final PackagePreferences r = mRestoredWithoutUids.valueAt(i); 715 writePackageXml(r, out, notifPermissions, false); 716 } 717 } 718 } 719 // Some apps have permissions set but don't have expanded notification settings 720 if (!notifPermissions.isEmpty()) { 721 for (Pair<Integer, String> app : notifPermissions.keySet()) { 722 out.startTag(null, TAG_PACKAGE); 723 out.attribute(null, ATT_NAME, app.second); 724 out.attributeInt(null, ATT_IMPORTANCE, 725 notifPermissions.get(app).first ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE); 726 out.endTag(null, TAG_PACKAGE); 727 } 728 } 729 out.endTag(null, TAG_RANKING); 730 } 731 732 public void writePackageXml(PackagePreferences r, TypedXmlSerializer out, 733 ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> notifPermissions, 734 boolean forBackup) throws 735 IOException { 736 out.startTag(null, TAG_PACKAGE); 737 out.attribute(null, ATT_NAME, r.pkg); 738 if (!notifPermissions.isEmpty()) { 739 Pair<Integer, String> app = new Pair(r.uid, r.pkg); 740 final Pair<Boolean, Boolean> permission = notifPermissions.get(app); 741 out.attributeInt(null, ATT_IMPORTANCE, 742 permission != null && permission.first ? IMPORTANCE_DEFAULT 743 : IMPORTANCE_NONE); 744 notifPermissions.remove(app); 745 } else { 746 if (r.importance != DEFAULT_IMPORTANCE) { 747 out.attributeInt(null, ATT_IMPORTANCE, r.importance); 748 } 749 } 750 if (r.priority != DEFAULT_PRIORITY) { 751 out.attributeInt(null, ATT_PRIORITY, r.priority); 752 } 753 if (r.visibility != DEFAULT_VISIBILITY) { 754 out.attributeInt(null, ATT_VISIBILITY, r.visibility); 755 } 756 if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) { 757 out.attributeInt(null, ATT_ALLOW_BUBBLE, r.bubblePreference); 758 } 759 out.attributeBoolean(null, ATT_SHOW_BADGE, r.showBadge); 760 out.attributeInt(null, ATT_APP_USER_LOCKED_FIELDS, 761 r.lockedAppFields); 762 out.attributeBoolean(null, ATT_SENT_INVALID_MESSAGE, 763 r.hasSentInvalidMessage); 764 out.attributeBoolean(null, ATT_SENT_VALID_MESSAGE, 765 r.hasSentValidMessage); 766 out.attributeBoolean(null, ATT_USER_DEMOTED_INVALID_MSG_APP, 767 r.userDemotedMsgApp); 768 out.attributeBoolean(null, ATT_SENT_VALID_BUBBLE, r.hasSentValidBubble); 769 if (android.app.Flags.uiRichOngoing()) { 770 if (r.canHavePromotedNotifs != DEFAULT_CAN_HAVE_PROMOTED_NOTIFS) { 771 out.attributeBoolean(null, ATT_PROMOTE_NOTIFS, r.canHavePromotedNotifs); 772 } 773 } 774 775 if (Flags.persistIncompleteRestoreData() && r.uid == UNKNOWN_UID) { 776 out.attributeLong(null, ATT_CREATION_TIME, r.creationTime); 777 out.attributeInt(null, ATT_USERID, r.userIdWhenUidUnknown); 778 } 779 780 if (!forBackup) { 781 out.attributeInt(null, ATT_UID, r.uid); 782 } 783 784 if (r.delegate != null) { 785 out.startTag(null, TAG_DELEGATE); 786 787 out.attribute(null, ATT_NAME, r.delegate.mPkg); 788 out.attributeInt(null, ATT_UID, r.delegate.mUid); 789 if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) { 790 out.attributeBoolean(null, ATT_ENABLED, r.delegate.mEnabled); 791 } 792 out.endTag(null, TAG_DELEGATE); 793 } 794 795 for (NotificationChannelGroup group : r.groups.values()) { 796 group.writeXml(out); 797 } 798 799 for (NotificationChannel channel : r.channels.values()) { 800 if (forBackup) { 801 if (!channel.isDeleted()) { 802 channel.writeXmlForBackup(out, mContext); 803 } 804 } else { 805 channel.writeXml(out); 806 } 807 } 808 809 out.endTag(null, TAG_PACKAGE); 810 } 811 812 /** 813 * Sets whether bubbles are allowed. 814 * 815 * @param pkg the package to allow or not allow bubbles for. 816 * @param uid the uid to allow or not allow bubbles for. 817 * @param bubblePreference whether bubbles are allowed. 818 */ 819 public void setBubblesAllowed(String pkg, int uid, int bubblePreference) { 820 boolean changed; 821 synchronized (mLock) { 822 PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid); 823 changed = p.bubblePreference != bubblePreference; 824 p.bubblePreference = bubblePreference; 825 p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE; 826 } 827 if (changed) { 828 updateConfig(); 829 } 830 } 831 832 /** 833 * Whether bubbles are allowed. 834 * 835 * @param pkg the package to check if bubbles are allowed for 836 * @param uid the uid to check if bubbles are allowed for. 837 * @return whether bubbles are allowed. 838 */ 839 @Override 840 public int getBubblePreference(String pkg, int uid) { 841 synchronized (mLock) { 842 return getOrCreatePackagePreferencesLocked(pkg, uid).bubblePreference; 843 } 844 } 845 846 public int getAppLockedFields(String pkg, int uid) { 847 synchronized (mLock) { 848 return getOrCreatePackagePreferencesLocked(pkg, uid).lockedAppFields; 849 } 850 } 851 852 @Override 853 public boolean canShowBadge(String packageName, int uid) { 854 synchronized (mLock) { 855 return getOrCreatePackagePreferencesLocked(packageName, uid).showBadge; 856 } 857 } 858 859 @Override 860 public void setShowBadge(String packageName, int uid, boolean showBadge) { 861 boolean changed = false; 862 synchronized (mLock) { 863 PackagePreferences pkgPrefs = getOrCreatePackagePreferencesLocked(packageName, uid); 864 if (pkgPrefs.showBadge != showBadge) { 865 pkgPrefs.showBadge = showBadge; 866 changed = true; 867 } 868 } 869 if (changed) { 870 updateConfig(); 871 } 872 } 873 874 @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING) 875 public boolean canBePromoted(String packageName, int uid) { 876 synchronized (mLock) { 877 return getOrCreatePackagePreferencesLocked(packageName, uid).canHavePromotedNotifs; 878 } 879 } 880 881 @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING) 882 public boolean setCanBePromoted(String packageName, int uid, boolean promote, 883 boolean fromUser) { 884 boolean changed = false; 885 synchronized (mLock) { 886 PackagePreferences pkgPrefs = getOrCreatePackagePreferencesLocked(packageName, uid); 887 if (fromUser || ((pkgPrefs.lockedAppFields & USER_LOCKED_PROMOTABLE) == 0)) { 888 if (pkgPrefs.canHavePromotedNotifs != promote) { 889 pkgPrefs.canHavePromotedNotifs = promote; 890 if (fromUser) { 891 pkgPrefs.lockedAppFields |= USER_LOCKED_PROMOTABLE; 892 } 893 changed = true; 894 } 895 } 896 } 897 // no need to send a ranking update because we need to update the flag value on all pending 898 // and posted notifs and NMS will take care of that 899 return changed; 900 } 901 902 public boolean isInInvalidMsgState(String packageName, int uid) { 903 synchronized (mLock) { 904 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid); 905 return r.hasSentInvalidMessage && !r.hasSentValidMessage; 906 } 907 } 908 909 public boolean hasUserDemotedInvalidMsgApp(String packageName, int uid) { 910 synchronized (mLock) { 911 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid); 912 return isInInvalidMsgState(packageName, uid) ? r.userDemotedMsgApp : false; 913 } 914 } 915 916 public void setInvalidMsgAppDemoted(String packageName, int uid, boolean isDemoted) { 917 synchronized (mLock) { 918 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid); 919 r.userDemotedMsgApp = isDemoted; 920 } 921 } 922 923 public boolean setInvalidMessageSent(String packageName, int uid) { 924 synchronized (mLock) { 925 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid); 926 boolean valueChanged = r.hasSentInvalidMessage == false; 927 r.hasSentInvalidMessage = true; 928 929 return valueChanged; 930 } 931 } 932 933 public boolean setValidMessageSent(String packageName, int uid) { 934 synchronized (mLock) { 935 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid); 936 boolean valueChanged = r.hasSentValidMessage == false; 937 r.hasSentValidMessage = true; 938 939 return valueChanged; 940 } 941 } 942 943 @VisibleForTesting 944 boolean hasSentInvalidMsg(String packageName, int uid) { 945 synchronized (mLock) { 946 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid); 947 return r.hasSentInvalidMessage; 948 } 949 } 950 951 @VisibleForTesting 952 boolean hasSentValidMsg(String packageName, int uid) { 953 synchronized (mLock) { 954 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid); 955 return r.hasSentValidMessage; 956 } 957 } 958 959 @VisibleForTesting 960 boolean didUserEverDemoteInvalidMsgApp(String packageName, int uid) { 961 synchronized (mLock) { 962 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid); 963 return r.userDemotedMsgApp; 964 } 965 } 966 967 /** Sets whether this package has sent a notification with valid bubble metadata. */ 968 public boolean setValidBubbleSent(String packageName, int uid) { 969 synchronized (mLock) { 970 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid); 971 boolean valueChanged = !r.hasSentValidBubble; 972 r.hasSentValidBubble = true; 973 return valueChanged; 974 } 975 } 976 977 boolean hasSentValidBubble(String packageName, int uid) { 978 synchronized (mLock) { 979 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid); 980 return r.hasSentValidBubble; 981 } 982 } 983 984 boolean isImportanceLocked(String pkg, int uid) { 985 synchronized (mLock) { 986 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); 987 return r.fixedImportance || r.defaultAppLockedImportance; 988 } 989 } 990 991 @Override 992 public boolean isGroupBlocked(String packageName, int uid, String groupId) { 993 if (groupId == null) { 994 return false; 995 } 996 synchronized (mLock) { 997 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid); 998 NotificationChannelGroup group = r.groups.get(groupId); 999 if (group == null) { 1000 return false; 1001 } 1002 return group.isBlocked(); 1003 } 1004 } 1005 1006 int getPackagePriority(String pkg, int uid) { 1007 synchronized (mLock) { 1008 return getOrCreatePackagePreferencesLocked(pkg, uid).priority; 1009 } 1010 } 1011 1012 int getPackageVisibility(String pkg, int uid) { 1013 synchronized (mLock) { 1014 return getOrCreatePackagePreferencesLocked(pkg, uid).visibility; 1015 } 1016 } 1017 1018 @Override 1019 public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group, 1020 boolean fromTargetApp, int callingUid, boolean fromSystemOrSystemUi) { 1021 Objects.requireNonNull(pkg); 1022 Objects.requireNonNull(group); 1023 Objects.requireNonNull(group.getId()); 1024 if (TextUtils.isEmpty(group.getName())) { 1025 throw new IllegalArgumentException("group.getName() can't be empty"); 1026 } 1027 boolean needsDndChange = false; 1028 boolean changed = false; 1029 synchronized (mLock) { 1030 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); 1031 if (r == null) { 1032 throw new IllegalArgumentException("Invalid package"); 1033 } 1034 if (r.groups.size() >= NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT) { 1035 throw new IllegalStateException("Limit exceed; cannot create more groups"); 1036 } 1037 if (fromTargetApp) { 1038 group.setBlocked(false); 1039 } 1040 final NotificationChannelGroup oldGroup = r.groups.get(group.getId()); 1041 if (oldGroup != null) { 1042 group.setChannels(oldGroup.getChannels()); 1043 1044 // apps can't update the blocked status or app overlay permission 1045 if (fromTargetApp) { 1046 group.setBlocked(oldGroup.isBlocked()); 1047 group.unlockFields(group.getUserLockedFields()); 1048 group.lockFields(oldGroup.getUserLockedFields()); 1049 } else { 1050 // but the system can 1051 if (group.isBlocked() != oldGroup.isBlocked()) { 1052 group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE); 1053 needsDndChange = true; 1054 } 1055 } 1056 } 1057 if (!group.equals(oldGroup)) { 1058 // will log for new entries as well as name/description changes 1059 changed = true; 1060 MetricsLogger.action(getChannelGroupLog(group.getId(), pkg)); 1061 mNotificationChannelLogger.logNotificationChannelGroup(group, uid, pkg, 1062 oldGroup == null, 1063 (oldGroup != null) && oldGroup.isBlocked()); 1064 } 1065 r.groups.put(group.getId(), group); 1066 } 1067 if (needsDndChange) { 1068 updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi); 1069 } 1070 if (android.app.Flags.nmBinderPerfCacheChannels() && changed) { 1071 invalidateNotificationChannelGroupCache(); 1072 } 1073 } 1074 1075 @Override 1076 public boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel, 1077 boolean fromTargetApp, boolean hasDndAccess, int callingUid, 1078 boolean fromSystemOrSystemUi) { 1079 Objects.requireNonNull(pkg); 1080 Objects.requireNonNull(channel); 1081 Objects.requireNonNull(channel.getId()); 1082 Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName())); 1083 Preconditions.checkArgument(channel.getImportance() >= IMPORTANCE_NONE 1084 && channel.getImportance() <= IMPORTANCE_MAX, "Invalid importance level"); 1085 1086 boolean needsPolicyFileChange = false, wasUndeleted = false, needsDndChange = false; 1087 synchronized (mLock) { 1088 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); 1089 if (r == null) { 1090 throw new IllegalArgumentException("Invalid package"); 1091 } 1092 if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) { 1093 throw new IllegalArgumentException("NotificationChannelGroup doesn't exist"); 1094 } 1095 if (DEFAULT_CHANNEL_ID.equals(channel.getId())) { 1096 throw new IllegalArgumentException("Reserved id"); 1097 } 1098 // Only the user can update bundle channel settings 1099 if (notificationClassification() && !fromSystemOrSystemUi 1100 && SYSTEM_RESERVED_IDS.contains(channel.getId())) { 1101 return false; 1102 } 1103 NotificationChannel existing = r.channels.get(channel.getId()); 1104 if (existing != null && fromTargetApp) { 1105 // Actually modifying an existing channel - keep most of the existing settings 1106 if (existing.isDeleted()) { 1107 // The existing channel was deleted - undelete it. 1108 existing.setDeleted(false); 1109 existing.setDeletedTimeMs(-1); 1110 needsPolicyFileChange = true; 1111 wasUndeleted = true; 1112 1113 // log a resurrected channel as if it's new again 1114 MetricsLogger.action(getChannelLog(channel, pkg).setType( 1115 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN)); 1116 mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg); 1117 } 1118 1119 if (!Objects.equals(channel.getName().toString(), existing.getName().toString())) { 1120 existing.setName(channel.getName().toString()); 1121 needsPolicyFileChange = true; 1122 } 1123 if (!Objects.equals(channel.getDescription(), existing.getDescription())) { 1124 existing.setDescription(channel.getDescription()); 1125 needsPolicyFileChange = true; 1126 } 1127 if (channel.isBlockable() != existing.isBlockable()) { 1128 existing.setBlockable(channel.isBlockable()); 1129 needsPolicyFileChange = true; 1130 } 1131 if (channel.getGroup() != null && existing.getGroup() == null) { 1132 existing.setGroup(channel.getGroup()); 1133 needsPolicyFileChange = true; 1134 } 1135 1136 // Apps are allowed to downgrade channel importance if the user has not changed any 1137 // fields on this channel yet. 1138 final int previousExistingImportance = existing.getImportance(); 1139 final int previousLoggingImportance = 1140 NotificationChannelLogger.getLoggingImportance(existing); 1141 if (existing.getUserLockedFields() == 0 && 1142 channel.getImportance() < existing.getImportance()) { 1143 existing.setImportance(channel.getImportance()); 1144 needsPolicyFileChange = true; 1145 } 1146 1147 // system apps and dnd access apps can bypass dnd if the user hasn't changed any 1148 // fields on the channel yet 1149 if (existing.getUserLockedFields() == 0 && hasDndAccess) { 1150 boolean bypassDnd = channel.canBypassDnd(); 1151 if (bypassDnd != existing.canBypassDnd() || wasUndeleted) { 1152 existing.setBypassDnd(bypassDnd); 1153 needsPolicyFileChange = true; 1154 1155 if (bypassDnd != mCurrentUserHasPriorityChannels 1156 || previousExistingImportance != existing.getImportance()) { 1157 needsDndChange = true; 1158 } 1159 } 1160 } 1161 1162 if (existing.getOriginalImportance() == IMPORTANCE_UNSPECIFIED) { 1163 existing.setOriginalImportance(channel.getImportance()); 1164 needsPolicyFileChange = true; 1165 } 1166 1167 if (needsPolicyFileChange) { 1168 updateConfig(); 1169 } 1170 if (needsPolicyFileChange && !wasUndeleted) { 1171 mNotificationChannelLogger.logNotificationChannelModified(existing, uid, pkg, 1172 previousLoggingImportance, false); 1173 } 1174 } else { 1175 if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) { 1176 throw new IllegalStateException("Limit exceed; cannot create more channels"); 1177 } 1178 1179 needsPolicyFileChange = true; 1180 1181 // Reset fields that apps aren't allowed to set. 1182 if (fromTargetApp && !hasDndAccess) { 1183 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX); 1184 } 1185 if (fromTargetApp) { 1186 channel.setLockscreenVisibility(r.visibility); 1187 channel.setAllowBubbles(existing != null 1188 ? existing.getAllowBubbles() 1189 : NotificationChannel.DEFAULT_ALLOW_BUBBLE); 1190 channel.setImportantConversation(false); 1191 } 1192 clearLockedFieldsLocked(channel); 1193 1194 channel.setImportanceLockedByCriticalDeviceFunction( 1195 r.defaultAppLockedImportance || r.fixedImportance); 1196 1197 if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) { 1198 channel.setLockscreenVisibility( 1199 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE); 1200 } 1201 if (!r.showBadge) { 1202 channel.setShowBadge(false); 1203 } 1204 channel.setOriginalImportance(channel.getImportance()); 1205 1206 // validate parent 1207 if (channel.getParentChannelId() != null) { 1208 Preconditions.checkArgument( 1209 r.channels.containsKey(channel.getParentChannelId()), 1210 "Tried to create a conversation channel without a preexisting parent"); 1211 } 1212 1213 r.channels.put(channel.getId(), channel); 1214 if (channel.canBypassDnd() != mCurrentUserHasPriorityChannels) { 1215 needsDndChange = true; 1216 } 1217 MetricsLogger.action(getChannelLog(channel, pkg).setType( 1218 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN)); 1219 mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg); 1220 } 1221 } 1222 1223 if (needsDndChange) { 1224 updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi); 1225 } 1226 1227 if (android.app.Flags.nmBinderPerfCacheChannels() && needsPolicyFileChange) { 1228 invalidateNotificationChannelCache(); 1229 } 1230 1231 return needsPolicyFileChange; 1232 } 1233 1234 void clearLockedFieldsLocked(NotificationChannel channel) { 1235 channel.unlockFields(channel.getUserLockedFields()); 1236 } 1237 1238 void unlockNotificationChannelImportance(String pkg, int uid, String updatedChannelId) { 1239 Objects.requireNonNull(updatedChannelId); 1240 synchronized (mLock) { 1241 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); 1242 if (r == null) { 1243 throw new IllegalArgumentException("Invalid package"); 1244 } 1245 1246 NotificationChannel channel = r.channels.get(updatedChannelId); 1247 if (channel == null || channel.isDeleted()) { 1248 throw new IllegalArgumentException("Channel does not exist"); 1249 } 1250 channel.unlockFields(USER_LOCKED_IMPORTANCE); 1251 } 1252 if (android.app.Flags.nmBinderPerfCacheChannels()) { 1253 invalidateNotificationChannelCache(); 1254 } 1255 } 1256 1257 1258 @Override 1259 public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel, 1260 boolean fromUser, int callingUid, boolean fromSystemOrSystemUi) { 1261 Objects.requireNonNull(updatedChannel); 1262 Objects.requireNonNull(updatedChannel.getId()); 1263 boolean changed = false; 1264 boolean needsDndChange = false; 1265 synchronized (mLock) { 1266 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); 1267 if (r == null) { 1268 throw new IllegalArgumentException("Invalid package"); 1269 } 1270 NotificationChannel channel = r.channels.get(updatedChannel.getId()); 1271 if (channel == null || channel.isDeleted()) { 1272 throw new IllegalArgumentException("Channel does not exist"); 1273 } 1274 if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) { 1275 updatedChannel.setLockscreenVisibility( 1276 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE); 1277 } 1278 if (fromUser) { 1279 updatedChannel.lockFields(channel.getUserLockedFields()); 1280 lockFieldsForUpdateLocked(channel, updatedChannel); 1281 } else { 1282 updatedChannel.unlockFields(updatedChannel.getUserLockedFields()); 1283 } 1284 1285 if (channel.isImportanceLockedByCriticalDeviceFunction() 1286 && !(channel.isBlockable() || channel.getImportance() == IMPORTANCE_NONE)) { 1287 updatedChannel.setImportance(channel.getImportance()); 1288 } 1289 1290 r.channels.put(updatedChannel.getId(), updatedChannel); 1291 1292 if (onlyHasDefaultChannel(pkg, uid)) { 1293 r.priority = updatedChannel.canBypassDnd() 1294 ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT; 1295 r.visibility = updatedChannel.getLockscreenVisibility(); 1296 r.showBadge = updatedChannel.canShowBadge(); 1297 changed = true; 1298 } 1299 1300 if (!channel.equals(updatedChannel)) { 1301 // only log if there are real changes 1302 MetricsLogger.action(getChannelLog(updatedChannel, pkg) 1303 .setSubtype(fromUser ? NOTIFICATION_UPDATE_LOG_SUBTYPE_FROM_USER 1304 : NOTIFICATION_UPDATE_LOG_SUBTYPE_FROM_APP)); 1305 mNotificationChannelLogger.logNotificationChannelModified(updatedChannel, uid, pkg, 1306 NotificationChannelLogger.getLoggingImportance(channel), fromUser); 1307 changed = true; 1308 } 1309 1310 if (fromUser && SystemUiSystemPropertiesFlags.getResolver().isEnabled( 1311 NotificationFlags.PROPAGATE_CHANNEL_UPDATES_TO_CONVERSATIONS)) { 1312 updateChildrenConversationChannels(r, channel, updatedChannel); 1313 // No need to update changed or needsDndChanged as the child channel(s) cannot be 1314 // relevantly affected without the parent channel already having been. 1315 } 1316 1317 if (updatedChannel.canBypassDnd() != mCurrentUserHasPriorityChannels 1318 || channel.getImportance() != updatedChannel.getImportance()) { 1319 needsDndChange = true; 1320 changed = true; 1321 } 1322 } 1323 if (needsDndChange) { 1324 updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi); 1325 } 1326 if (changed) { 1327 if (android.app.Flags.nmBinderPerfCacheChannels()) { 1328 invalidateNotificationChannelCache(); 1329 } 1330 updateConfig(); 1331 } 1332 } 1333 1334 /** 1335 * Updates conversation channels after user changes to their parent channel. See 1336 * {@link #maybeUpdateChildConversationChannel}. 1337 */ 1338 @GuardedBy("mPackagePreferences") 1339 private void updateChildrenConversationChannels(@NonNull PackagePreferences packagePreferences, 1340 @NonNull NotificationChannel oldParent, @NonNull NotificationChannel updatedParent) { 1341 if (oldParent.equals(updatedParent)) { 1342 return; 1343 } 1344 if (oldParent.isConversation()) { 1345 return; // Can't have children. 1346 } 1347 for (NotificationChannel channel : packagePreferences.channels.values()) { 1348 // Include deleted -- otherwise they will have old settings if later resurrected. 1349 // Include demoted -- still attached to their parents. 1350 if (channel.isConversation() 1351 && oldParent.getId().equals(channel.getParentChannelId())) { 1352 maybeUpdateChildConversationChannel(packagePreferences.pkg, packagePreferences.uid, 1353 channel, oldParent, updatedParent); 1354 } 1355 } 1356 } 1357 1358 /** 1359 * Apply the diff between {@code oldParent} and {@code updatedParent} to the child 1360 * {@code conversation} channel. Only fields that are not locked on the conversation channel 1361 * (see {@link NotificationChannel#LOCKABLE_FIELDS }) will be updated (so that we don't override 1362 * previous explicit user choices). 1363 * 1364 * <p>This will also log the change as if it was {@code fromUser=true}. 1365 */ 1366 @GuardedBy("mPackagePreferences") 1367 private void maybeUpdateChildConversationChannel(String pkg, int uid, 1368 @NonNull NotificationChannel conversation, @NonNull NotificationChannel oldParent, 1369 @NonNull NotificationChannel updatedParent) { 1370 boolean changed = false; 1371 int oldLoggingImportance = NotificationChannelLogger.getLoggingImportance(conversation); 1372 1373 if ((conversation.getUserLockedFields() & NotificationChannel.USER_LOCKED_PRIORITY) == 0 1374 && oldParent.canBypassDnd() != updatedParent.canBypassDnd()) { 1375 conversation.setBypassDnd(updatedParent.canBypassDnd()); 1376 changed = true; 1377 } 1378 if ((conversation.getUserLockedFields() & NotificationChannel.USER_LOCKED_VISIBILITY) == 0 1379 && oldParent.getLockscreenVisibility() 1380 != updatedParent.getLockscreenVisibility()) { 1381 conversation.setLockscreenVisibility(updatedParent.getLockscreenVisibility()); 1382 changed = true; 1383 } 1384 if ((conversation.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0 1385 && oldParent.getImportance() != updatedParent.getImportance()) { 1386 conversation.setImportance(updatedParent.getImportance()); 1387 changed = true; 1388 } 1389 if ((conversation.getUserLockedFields() & NotificationChannel.USER_LOCKED_LIGHTS) == 0 1390 && (oldParent.shouldShowLights() != updatedParent.shouldShowLights() 1391 || oldParent.getLightColor() != updatedParent.getLightColor())) { 1392 conversation.enableLights(updatedParent.shouldShowLights()); 1393 conversation.setLightColor(updatedParent.getLightColor()); 1394 changed = true; 1395 } 1396 if ((conversation.getUserLockedFields() & NotificationChannel.USER_LOCKED_SOUND) == 0 1397 && !Objects.equals(oldParent.getSound(), updatedParent.getSound())) { 1398 conversation.setSound(updatedParent.getSound(), updatedParent.getAudioAttributes()); 1399 changed = true; 1400 } 1401 if ((conversation.getUserLockedFields() & NotificationChannel.USER_LOCKED_VIBRATION) == 0 1402 && (!Arrays.equals(oldParent.getVibrationPattern(), 1403 updatedParent.getVibrationPattern()) 1404 || !Objects.equals( 1405 oldParent.getVibrationEffect(), updatedParent.getVibrationEffect()) 1406 || oldParent.shouldVibrate() != updatedParent.shouldVibrate())) { 1407 // enableVibration must be 2nd because setVibrationPattern may toggle it. 1408 conversation.setVibrationPattern(updatedParent.getVibrationPattern()); 1409 conversation.enableVibration(updatedParent.shouldVibrate()); 1410 changed = true; 1411 } 1412 if ((conversation.getUserLockedFields() & NotificationChannel.USER_LOCKED_SHOW_BADGE) == 0 1413 && oldParent.canShowBadge() != updatedParent.canShowBadge()) { 1414 conversation.setShowBadge(updatedParent.canShowBadge()); 1415 changed = true; 1416 } 1417 if ((conversation.getUserLockedFields() & NotificationChannel.USER_LOCKED_ALLOW_BUBBLE) == 0 1418 && oldParent.getAllowBubbles() != updatedParent.getAllowBubbles()) { 1419 conversation.setAllowBubbles(updatedParent.getAllowBubbles()); 1420 changed = true; 1421 } 1422 1423 if (changed) { 1424 MetricsLogger.action(getChannelLog(conversation, pkg).setSubtype( 1425 NOTIFICATION_UPDATE_LOG_SUBTYPE_FROM_USER)); 1426 mNotificationChannelLogger.logNotificationChannelModified(conversation, uid, pkg, 1427 oldLoggingImportance, /* fromUser= */ true); 1428 } 1429 } 1430 1431 @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) 1432 public NotificationChannel getReservedChannel(String pkg, int uid, 1433 @Adjustment.Types int type) { 1434 if (!notificationClassification()) { 1435 return null; 1436 } 1437 Objects.requireNonNull(pkg); 1438 String channelId = NotificationChannel.getChannelIdForBundleType(type); 1439 if (channelId == null) { 1440 return null; 1441 } 1442 NotificationChannel channel = 1443 getConversationNotificationChannel(pkg, uid, channelId, null, true, false); 1444 return channel; 1445 } 1446 1447 @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) 1448 public NotificationChannel createReservedChannel(String pkg, int uid, 1449 @Adjustment.Types int type) { 1450 if (!notificationClassification()) { 1451 return null; 1452 } 1453 Objects.requireNonNull(pkg); 1454 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); 1455 if (r == null) { 1456 return null; 1457 } 1458 String channelId = NotificationChannel.getChannelIdForBundleType(type); 1459 if (channelId == null) { 1460 return null; 1461 } 1462 return addReservedChannelLocked(r, channelId); 1463 } 1464 1465 @Override 1466 public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, 1467 boolean includeDeleted) { 1468 Objects.requireNonNull(pkg); 1469 return getConversationNotificationChannel(pkg, uid, channelId, null, true, includeDeleted); 1470 } 1471 1472 @Override 1473 public NotificationChannel getConversationNotificationChannel(String pkg, int uid, 1474 String channelId, String conversationId, boolean returnParentIfNoConversationChannel, 1475 boolean includeDeleted) { 1476 Preconditions.checkNotNull(pkg); 1477 synchronized (mLock) { 1478 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); 1479 if (r == null) { 1480 return null; 1481 } 1482 if (channelId == null) { 1483 channelId = DEFAULT_CHANNEL_ID; 1484 } 1485 NotificationChannel channel = null; 1486 if (conversationId != null) { 1487 // look for an automatically created conversation specific channel 1488 channel = findConversationChannel(r, channelId, conversationId, includeDeleted); 1489 } 1490 if (channel == null && returnParentIfNoConversationChannel) { 1491 // look for it just based on its id 1492 final NotificationChannel nc = r.channels.get(channelId); 1493 if (nc != null && (includeDeleted || !nc.isDeleted())) { 1494 return nc; 1495 } 1496 } 1497 return channel; 1498 } 1499 } 1500 1501 private NotificationChannel findConversationChannel(PackagePreferences p, String parentId, 1502 String conversationId, boolean includeDeleted) { 1503 for (NotificationChannel nc : p.channels.values()) { 1504 if (conversationId.equals(nc.getConversationId()) 1505 && parentId.equals(nc.getParentChannelId()) 1506 && (includeDeleted || !nc.isDeleted())) { 1507 return nc; 1508 } 1509 } 1510 return null; 1511 } 1512 1513 public List<NotificationChannel> getNotificationChannelsByConversationId(String pkg, int uid, 1514 String conversationId) { 1515 Preconditions.checkNotNull(pkg); 1516 Preconditions.checkNotNull(conversationId); 1517 List<NotificationChannel> channels = new ArrayList<>(); 1518 synchronized (mLock) { 1519 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); 1520 if (r == null) { 1521 return channels; 1522 } 1523 for (NotificationChannel nc : r.channels.values()) { 1524 if (conversationId.equals(nc.getConversationId()) 1525 && !nc.isDeleted()) { 1526 channels.add(nc); 1527 } 1528 } 1529 return channels; 1530 } 1531 } 1532 1533 @Override 1534 public boolean deleteNotificationChannel(String pkg, int uid, String channelId, 1535 int callingUid, boolean fromSystemOrSystemUi) { 1536 boolean deletedChannel = false; 1537 boolean channelBypassedDnd = false; 1538 synchronized (mLock) { 1539 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 1540 if (r == null) { 1541 return false; 1542 } 1543 NotificationChannel channel = r.channels.get(channelId); 1544 if (channel != null) { 1545 channelBypassedDnd = channel.canBypassDnd(); 1546 deletedChannel = deleteNotificationChannelLocked(channel, pkg, uid); 1547 } 1548 } 1549 if (channelBypassedDnd) { 1550 updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi); 1551 } 1552 1553 if (android.app.Flags.nmBinderPerfCacheChannels() && deletedChannel) { 1554 invalidateNotificationChannelCache(); 1555 } 1556 return deletedChannel; 1557 } 1558 1559 private boolean deleteNotificationChannelLocked(NotificationChannel channel, String pkg, 1560 int uid) { 1561 if (!channel.isDeleted()) { 1562 channel.setDeleted(true); 1563 channel.setDeletedTimeMs(System.currentTimeMillis()); 1564 LogMaker lm = getChannelLog(channel, pkg); 1565 lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE); 1566 MetricsLogger.action(lm); 1567 mNotificationChannelLogger.logNotificationChannelDeleted(channel, uid, pkg); 1568 return true; 1569 } 1570 return false; 1571 } 1572 1573 @Override 1574 @VisibleForTesting 1575 public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) { 1576 Objects.requireNonNull(pkg); 1577 Objects.requireNonNull(channelId); 1578 synchronized (mLock) { 1579 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 1580 if (r == null) { 1581 return; 1582 } 1583 r.channels.remove(channelId); 1584 } 1585 if (android.app.Flags.nmBinderPerfCacheChannels()) { 1586 invalidateNotificationChannelCache(); 1587 } 1588 } 1589 1590 @Override 1591 public void permanentlyDeleteNotificationChannels(String pkg, int uid) { 1592 Objects.requireNonNull(pkg); 1593 synchronized (mLock) { 1594 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 1595 if (r == null) { 1596 return; 1597 } 1598 boolean deleted = false; 1599 int N = r.channels.size() - 1; 1600 for (int i = N; i >= 0; i--) { 1601 String key = r.channels.keyAt(i); 1602 if (!DEFAULT_CHANNEL_ID.equals(key)) { 1603 r.channels.remove(key); 1604 deleted = true; 1605 } 1606 } 1607 if (android.app.Flags.nmBinderPerfCacheChannels() && deleted) { 1608 invalidateNotificationChannelCache(); 1609 } 1610 } 1611 } 1612 1613 public boolean shouldHideSilentStatusIcons() { 1614 return mHideSilentStatusBarIcons; 1615 } 1616 1617 public void setHideSilentStatusIcons(boolean hide) { 1618 mHideSilentStatusBarIcons = hide; 1619 } 1620 1621 public void updateFixedImportance(List<UserInfo> users) { 1622 for (UserInfo user : users) { 1623 List<PackageInfo> packages = mPm.getInstalledPackagesAsUser( 1624 0, user.getUserHandle().getIdentifier()); 1625 for (PackageInfo pi : packages) { 1626 boolean fixed = mPermissionHelper.isPermissionFixed( 1627 pi.packageName, user.getUserHandle().getIdentifier()); 1628 if (fixed) { 1629 synchronized (mLock) { 1630 PackagePreferences p = getOrCreatePackagePreferencesLocked( 1631 pi.packageName, pi.applicationInfo.uid); 1632 p.fixedImportance = true; 1633 for (NotificationChannel channel : p.channels.values()) { 1634 channel.setImportanceLockedByCriticalDeviceFunction(true); 1635 } 1636 } 1637 } 1638 } 1639 } 1640 if (android.app.Flags.nmBinderPerfCacheChannels()) { 1641 invalidateNotificationChannelCache(); 1642 } 1643 } 1644 1645 public void updateDefaultApps(int userId, ArraySet<String> toRemove, 1646 ArraySet<Pair<String, Integer>> toAdd) { 1647 synchronized (mLock) { 1648 for (PackagePreferences p : mPackagePreferences.values()) { 1649 if (userId == UserHandle.getUserId(p.uid)) { 1650 if (toRemove != null && toRemove.contains(p.pkg)) { 1651 p.defaultAppLockedImportance = false; 1652 if (!p.fixedImportance) { 1653 for (NotificationChannel channel : p.channels.values()) { 1654 channel.setImportanceLockedByCriticalDeviceFunction(false); 1655 } 1656 } 1657 } 1658 } 1659 } 1660 if (toAdd != null) { 1661 for (Pair<String, Integer> approvedApp : toAdd) { 1662 PackagePreferences p = getOrCreatePackagePreferencesLocked( 1663 approvedApp.first, 1664 approvedApp.second); 1665 p.defaultAppLockedImportance = true; 1666 for (NotificationChannel channel : p.channels.values()) { 1667 channel.setImportanceLockedByCriticalDeviceFunction(true); 1668 } 1669 } 1670 } 1671 } 1672 if (android.app.Flags.nmBinderPerfCacheChannels()) { 1673 invalidateNotificationChannelCache(); 1674 } 1675 } 1676 1677 public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg, 1678 int uid, String groupId, boolean includeDeleted) { 1679 Objects.requireNonNull(pkg); 1680 synchronized (mLock) { 1681 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 1682 if (r == null || groupId == null || !r.groups.containsKey(groupId)) { 1683 return null; 1684 } 1685 return NotificationChannelGroupsHelper.getGroupWithChannels(groupId, 1686 r.channels.values(), r.groups, includeDeleted); 1687 } 1688 } 1689 1690 public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg, 1691 int uid) { 1692 Objects.requireNonNull(pkg); 1693 synchronized (mLock) { 1694 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 1695 if (r == null) { 1696 return null; 1697 } 1698 return r.groups.get(groupId); 1699 } 1700 } 1701 1702 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg, 1703 int uid, NotificationChannelGroupsHelper.Params params) { 1704 Objects.requireNonNull(pkg); 1705 synchronized (mLock) { 1706 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 1707 if (r == null) { 1708 return ParceledListSlice.emptyList(); 1709 } 1710 return new ParceledListSlice<>( 1711 NotificationChannelGroupsHelper.getGroupsWithChannels(r.channels.values(), 1712 r.groups, params)); 1713 } 1714 } 1715 1716 public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid, 1717 String groupId, int callingUid, boolean fromSystemOrSystemUi) { 1718 List<NotificationChannel> deletedChannels = new ArrayList<>(); 1719 boolean groupBypassedDnd = false; 1720 boolean deleted = false; 1721 synchronized (mLock) { 1722 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 1723 if (r == null || TextUtils.isEmpty(groupId)) { 1724 return deletedChannels; 1725 } 1726 1727 NotificationChannelGroup channelGroup = r.groups.remove(groupId); 1728 if (channelGroup != null) { 1729 deleted = true; 1730 mNotificationChannelLogger.logNotificationChannelGroupDeleted(channelGroup, uid, 1731 pkg); 1732 } 1733 1734 int N = r.channels.size(); 1735 for (int i = 0; i < N; i++) { 1736 final NotificationChannel nc = r.channels.valueAt(i); 1737 if (groupId.equals(nc.getGroup())) { 1738 groupBypassedDnd |= nc.canBypassDnd(); 1739 deleteNotificationChannelLocked(nc, pkg, uid); 1740 deletedChannels.add(nc); 1741 } 1742 } 1743 } 1744 if (groupBypassedDnd) { 1745 updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi); 1746 } 1747 if (android.app.Flags.nmBinderPerfCacheChannels()) { 1748 if (deletedChannels.size() > 0) { 1749 invalidateNotificationChannelCache(); 1750 } 1751 if (deleted) { 1752 invalidateNotificationChannelGroupCache(); 1753 } 1754 } 1755 return deletedChannels; 1756 } 1757 1758 /** 1759 * Returns all notification channel groups for the provided package and uid, without channel 1760 * information included. Note that this method returns the object instances from the internal 1761 * structure; do not modify the returned groups before copying or parceling. 1762 */ 1763 @Override 1764 public Collection<NotificationChannelGroup> getNotificationChannelGroupsWithoutChannels( 1765 String pkg, int uid) { 1766 List<NotificationChannelGroup> groups = new ArrayList<>(); 1767 synchronized (mLock) { 1768 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 1769 if (r == null) { 1770 return groups; 1771 } 1772 groups.addAll(r.groups.values()); 1773 } 1774 return groups; 1775 } 1776 1777 public NotificationChannelGroup getGroupForChannel(String pkg, int uid, String channelId) { 1778 synchronized (mLock) { 1779 PackagePreferences p = getPackagePreferencesLocked(pkg, uid); 1780 if (p != null) { 1781 NotificationChannel nc = p.channels.get(channelId); 1782 if (nc != null && !nc.isDeleted()) { 1783 if (nc.getGroup() != null) { 1784 NotificationChannelGroup group = p.groups.get(nc.getGroup()); 1785 if (group != null) { 1786 return group; 1787 } 1788 } 1789 } 1790 } 1791 } 1792 return null; 1793 } 1794 1795 public ArrayList<ConversationChannelWrapper> getConversations(IntArray userIds, 1796 boolean onlyImportant) { 1797 synchronized (mLock) { 1798 ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>(); 1799 for (PackagePreferences p : mPackagePreferences.values()) { 1800 if (userIds.binarySearch(UserHandle.getUserId(p.uid)) >= 0) { 1801 int N = p.channels.size(); 1802 for (int i = 0; i < N; i++) { 1803 final NotificationChannel nc = p.channels.valueAt(i); 1804 if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted() 1805 && !nc.isDemoted() 1806 && (nc.isImportantConversation() || !onlyImportant)) { 1807 ConversationChannelWrapper conversation = 1808 new ConversationChannelWrapper(); 1809 conversation.setPkg(p.pkg); 1810 conversation.setUid(p.uid); 1811 conversation.setNotificationChannel(nc); 1812 NotificationChannel parent = p.channels.get(nc.getParentChannelId()); 1813 conversation.setParentChannelLabel(parent == null 1814 ? null 1815 : parent.getName()); 1816 boolean blockedByGroup = false; 1817 if (nc.getGroup() != null) { 1818 NotificationChannelGroup group = p.groups.get(nc.getGroup()); 1819 if (group != null) { 1820 if (group.isBlocked()) { 1821 blockedByGroup = true; 1822 } else { 1823 conversation.setGroupLabel(group.getName()); 1824 } 1825 } 1826 } 1827 if (!blockedByGroup) { 1828 conversations.add(conversation); 1829 } 1830 } 1831 } 1832 } 1833 } 1834 1835 return conversations; 1836 } 1837 } 1838 1839 public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) { 1840 Objects.requireNonNull(pkg); 1841 synchronized (mLock) { 1842 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 1843 if (r == null) { 1844 return new ArrayList<>(); 1845 } 1846 ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>(); 1847 int N = r.channels.size(); 1848 for (int i = 0; i < N; i++) { 1849 final NotificationChannel nc = r.channels.valueAt(i); 1850 if (!TextUtils.isEmpty(nc.getConversationId()) 1851 && !nc.isDeleted() 1852 && !nc.isDemoted()) { 1853 ConversationChannelWrapper conversation = new ConversationChannelWrapper(); 1854 conversation.setPkg(r.pkg); 1855 conversation.setUid(r.uid); 1856 conversation.setNotificationChannel(nc); 1857 conversation.setParentChannelLabel( 1858 r.channels.get(nc.getParentChannelId()).getName()); 1859 boolean blockedByGroup = false; 1860 if (nc.getGroup() != null) { 1861 NotificationChannelGroup group = r.groups.get(nc.getGroup()); 1862 if (group != null) { 1863 if (group.isBlocked()) { 1864 blockedByGroup = true; 1865 } else { 1866 conversation.setGroupLabel(group.getName()); 1867 } 1868 } 1869 } 1870 if (!blockedByGroup) { 1871 conversations.add(conversation); 1872 } 1873 } 1874 } 1875 1876 return conversations; 1877 } 1878 } 1879 1880 public @NonNull List<String> deleteConversations(String pkg, int uid, 1881 Set<String> conversationIds, int callingUid, boolean fromSystemOrSystemUi) { 1882 List<String> deletedChannelIds = new ArrayList<>(); 1883 synchronized (mLock) { 1884 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 1885 if (r == null) { 1886 return deletedChannelIds; 1887 } 1888 int N = r.channels.size(); 1889 for (int i = 0; i < N; i++) { 1890 final NotificationChannel nc = r.channels.valueAt(i); 1891 if (nc.getConversationId() != null 1892 && conversationIds.contains(nc.getConversationId())) { 1893 nc.setDeleted(true); 1894 nc.setDeletedTimeMs(System.currentTimeMillis()); 1895 LogMaker lm = getChannelLog(nc, pkg); 1896 lm.setType( 1897 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE); 1898 MetricsLogger.action(lm); 1899 mNotificationChannelLogger.logNotificationChannelDeleted(nc, uid, pkg); 1900 1901 deletedChannelIds.add(nc.getId()); 1902 } 1903 } 1904 } 1905 if (!deletedChannelIds.isEmpty()) { 1906 if (mCurrentUserHasPriorityChannels) { 1907 updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi); 1908 } 1909 if (android.app.Flags.nmBinderPerfCacheChannels()) { 1910 invalidateNotificationChannelCache(); 1911 } 1912 } 1913 return deletedChannelIds; 1914 } 1915 1916 @Override 1917 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, 1918 boolean includeDeleted, boolean includeBundles) { 1919 Objects.requireNonNull(pkg); 1920 List<NotificationChannel> channels = new ArrayList<>(); 1921 synchronized (mLock) { 1922 PackagePreferences r; 1923 if (android.app.Flags.nmBinderPerfCacheChannels()) { 1924 r = getOrCreatePackagePreferencesLocked(pkg, uid); 1925 } else { 1926 r = getPackagePreferencesLocked(pkg, uid); 1927 } 1928 if (r == null) { 1929 return ParceledListSlice.emptyList(); 1930 } 1931 int N = r.channels.size(); 1932 for (int i = 0; i < N; i++) { 1933 final NotificationChannel nc = r.channels.valueAt(i); 1934 if (includeDeleted || !nc.isDeleted()) { 1935 if (includeBundles || !SYSTEM_RESERVED_IDS.contains(nc.getId())) { 1936 channels.add(nc); 1937 } 1938 } 1939 } 1940 return new ParceledListSlice<>(channels); 1941 } 1942 } 1943 1944 @VisibleForTesting(otherwise = VisibleForTesting.NONE) 1945 // Gets the entire list of notification channels for this package, with no filtering and 1946 // without creating package preferences. For testing only, specifically to confirm the 1947 // notification channels of a removed/deleted package. 1948 protected List<NotificationChannel> getRemovedPkgNotificationChannels(String pkg, int uid) { 1949 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 1950 if (r == null || r.channels == null) { 1951 return new ArrayList<>(); 1952 } 1953 return new ArrayList<>(r.channels.values()); 1954 } 1955 1956 /** 1957 * Gets all notification channels associated with the given pkg and uid that can bypass dnd 1958 */ 1959 public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg, 1960 int uid) { 1961 List<NotificationChannel> channels = new ArrayList<>(); 1962 synchronized (mLock) { 1963 final PackagePreferences r = mPackagePreferences.get( 1964 packagePreferencesKey(pkg, uid)); 1965 if (r != null) { 1966 for (NotificationChannel channel : r.channels.values()) { 1967 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) { 1968 channels.add(channel); 1969 } 1970 } 1971 } 1972 } 1973 return new ParceledListSlice<>(channels); 1974 } 1975 1976 /** 1977 * Gets all apps that can bypass DND, and a boolean indicating whether all (true) or some 1978 * (false) of its notification channels can currently bypass. 1979 */ 1980 public @NonNull ArrayList<ZenBypassingApp> getPackagesBypassingDnd(@UserIdInt int userId) { 1981 ArrayList<ZenBypassingApp> bypassing = new ArrayList<>(); 1982 synchronized (mLock) { 1983 for (PackagePreferences p : mPackagePreferences.values()) { 1984 if (UserHandle.getUserId(p.uid) != userId) { 1985 continue; 1986 } 1987 int totalChannelCount = p.channels.size(); 1988 int bypassingCount = 0; 1989 if (totalChannelCount == 0) { 1990 continue; 1991 } 1992 for (NotificationChannel channel : p.channels.values()) { 1993 if (channelIsLiveLocked(p, channel) && channel.canBypassDnd()) { 1994 bypassingCount++; 1995 } 1996 } 1997 if (bypassingCount > 0) { 1998 bypassing.add(new ZenBypassingApp(p.pkg, totalChannelCount == bypassingCount)); 1999 } 2000 } 2001 } 2002 return bypassing; 2003 } 2004 2005 /** 2006 * Gets all apps for this user that have a nonzero number of channels. This count does not 2007 * include deleted channels. 2008 */ 2009 @FlaggedApi(android.app.Flags.FLAG_NM_BINDER_PERF_GET_APPS_WITH_CHANNELS) 2010 public @NonNull List<String> getPackagesWithAnyChannels(@UserIdInt int userId) { 2011 List<String> pkgs = new ArrayList<>(); 2012 synchronized (mLock) { 2013 for (PackagePreferences p : mPackagePreferences.values()) { 2014 if (UserHandle.getUserId(p.uid) != userId) { 2015 continue; 2016 } 2017 for (NotificationChannel c : p.channels.values()) { 2018 if (!c.isDeleted()) { 2019 pkgs.add(p.pkg); 2020 break; 2021 } 2022 } 2023 } 2024 } 2025 return pkgs; 2026 } 2027 2028 /** 2029 * True for pre-O apps that only have the default channel, or pre O apps that have no 2030 * channels yet. This method will create the default channel for pre-O apps that don't have it. 2031 * Should never be true for O+ targeting apps, but that's enforced on boot/when an app 2032 * upgrades. 2033 */ 2034 public boolean onlyHasDefaultChannel(String pkg, int uid) { 2035 synchronized (mLock) { 2036 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); 2037 if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) { 2038 if (r.channels.size() == 1) { 2039 return true; 2040 } 2041 if (notificationClassification()) { 2042 if (r.channels.size() <= 5) { 2043 for (NotificationChannel c : r.channels.values()) { 2044 if (!SYSTEM_RESERVED_IDS.contains(c.getId()) && 2045 !DEFAULT_CHANNEL_ID.equals(c.getId())) { 2046 return false; 2047 } 2048 return true; 2049 } 2050 } else { 2051 return false; 2052 } 2053 } 2054 } 2055 return false; 2056 } 2057 } 2058 2059 public int getDeletedChannelCount(String pkg, int uid) { 2060 Objects.requireNonNull(pkg); 2061 int deletedCount = 0; 2062 synchronized (mLock) { 2063 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 2064 if (r == null) { 2065 return deletedCount; 2066 } 2067 int N = r.channels.size(); 2068 for (int i = 0; i < N; i++) { 2069 final NotificationChannel nc = r.channels.valueAt(i); 2070 if (nc.isDeleted()) { 2071 deletedCount++; 2072 } 2073 } 2074 return deletedCount; 2075 } 2076 } 2077 2078 public int getBlockedChannelCount(String pkg, int uid) { 2079 Objects.requireNonNull(pkg); 2080 int blockedCount = 0; 2081 synchronized (mLock) { 2082 PackagePreferences r = getPackagePreferencesLocked(pkg, uid); 2083 if (r == null) { 2084 return blockedCount; 2085 } 2086 int N = r.channels.size(); 2087 for (int i = 0; i < N; i++) { 2088 final NotificationChannel nc = r.channels.valueAt(i); 2089 if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) { 2090 blockedCount++; 2091 } 2092 } 2093 return blockedCount; 2094 } 2095 } 2096 2097 /** 2098 * Syncs {@link #mCurrentUserHasPriorityChannels} with the current user's notification 2099 * policy before updating. Must be called: 2100 * <ul> 2101 * <li>On system init, after channels and DND configurations are loaded. 2102 * <li>When the current user is switched, after the corresponding DND config is loaded. 2103 * <li>If users are removed (the removed user could've been a profile of the current one). 2104 * </ul> 2105 */ 2106 void syncHasPriorityChannels() { 2107 mCurrentUserHasPriorityChannels = 2108 (mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT).state 2109 & NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS) != 0; 2110 2111 updateCurrentUserHasPriorityChannels(/* callingUid= */ Process.SYSTEM_UID, 2112 /* fromSystemOrSystemUi= */ true); 2113 } 2114 2115 /** 2116 * Updates the user's NotificationPolicy based on whether the current userId has channels 2117 * marked as "priority" (which might bypass DND, depending on the zen rule details). It should 2118 * be called whenever a channel is created, updated, or deleted, or when the current user (or 2119 * its profiles) change. 2120 */ 2121 // TODO: b/368247671 - remove fromSystemOrSystemUi argument when modes_ui is inlined. 2122 private void updateCurrentUserHasPriorityChannels(int callingUid, 2123 boolean fromSystemOrSystemUi) { 2124 ArraySet<Pair<String, Integer>> candidatePkgs = new ArraySet<>(); 2125 2126 final IntArray currentUserIds = mUserProfiles.getCurrentProfileIds(); 2127 synchronized (mLock) { 2128 final int numPackagePreferences = mPackagePreferences.size(); 2129 for (int i = 0; i < numPackagePreferences; i++) { 2130 final PackagePreferences r = mPackagePreferences.valueAt(i); 2131 if (!currentUserIds.contains(UserHandle.getUserId(r.uid))) { 2132 continue; // Package isn't associated with any profile of the current userId. 2133 } 2134 2135 for (NotificationChannel channel : r.channels.values()) { 2136 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) { 2137 candidatePkgs.add(new Pair<>(r.pkg, r.uid)); 2138 break; 2139 } 2140 } 2141 } 2142 } 2143 for (int i = candidatePkgs.size() - 1; i >= 0; i--) { 2144 Pair<String, Integer> app = candidatePkgs.valueAt(i); 2145 if (!mPermissionHelper.hasPermission(app.second)) { 2146 candidatePkgs.removeAt(i); 2147 } 2148 } 2149 boolean haveBypassingApps = candidatePkgs.size() > 0; 2150 if (mCurrentUserHasPriorityChannels != haveBypassingApps) { 2151 mCurrentUserHasPriorityChannels = haveBypassingApps; 2152 if (android.app.Flags.modesUi()) { 2153 mZenModeHelper.updateHasPriorityChannels(UserHandle.CURRENT, 2154 mCurrentUserHasPriorityChannels); 2155 } else { 2156 updateZenPolicy(mCurrentUserHasPriorityChannels, callingUid, 2157 fromSystemOrSystemUi); 2158 } 2159 } 2160 } 2161 2162 private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) { 2163 // Channel is in a group that's blocked 2164 if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) { 2165 return false; 2166 } 2167 2168 // Channel is deleted or is blocked 2169 if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) { 2170 return false; 2171 } 2172 2173 return true; 2174 } 2175 2176 // TODO: b/368247671 - delete this method when modes_ui is inlined, as 2177 // updateCurrentUserHasChannelsBypassingDnd was the only caller and 2178 // PreferencesHelper should otherwise not need to modify actual policy 2179 public void updateZenPolicy(boolean areChannelsBypassingDnd, int callingUid, 2180 boolean fromSystemOrSystemUi) { 2181 NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy( 2182 UserHandle.CURRENT); 2183 mZenModeHelper.setNotificationPolicy( 2184 UserHandle.CURRENT, 2185 new NotificationManager.Policy( 2186 policy.priorityCategories, policy.priorityCallSenders, 2187 policy.priorityMessageSenders, policy.suppressedVisualEffects, 2188 (areChannelsBypassingDnd 2189 ? NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS : 0), 2190 policy.priorityConversationSenders), 2191 fromSystemOrSystemUi ? ZenModeConfig.ORIGIN_SYSTEM 2192 : ZenModeConfig.ORIGIN_APP, 2193 callingUid); 2194 } 2195 2196 /** 2197 * Whether the current user has any channels marked as "priority channels" 2198 * ({@link NotificationChannel#canBypassDnd}), but not necessarily whether they are permitted 2199 * to bypass the filters set by the current zen policy. 2200 */ 2201 public boolean hasPriorityChannels() { 2202 return mCurrentUserHasPriorityChannels; 2203 } 2204 2205 /** 2206 * Sets whether any notifications from the app, represented by the given {@code pkgName} and 2207 * {@code uid}, have their importance locked by the user. Locked notifications don't get 2208 * considered for sentiment adjustments (and thus never show a blocking helper). 2209 */ 2210 public void setAppImportanceLocked(String packageName, int uid) { 2211 synchronized (mLock) { 2212 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(packageName, uid); 2213 if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) { 2214 return; 2215 } 2216 2217 prefs.lockedAppFields = 2218 prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE; 2219 } 2220 updateConfig(); 2221 } 2222 2223 /** 2224 * Returns the delegate for a given package, if it's allowed by the package and the user. 2225 */ 2226 public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) { 2227 synchronized (mLock) { 2228 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid); 2229 2230 if (prefs == null || prefs.delegate == null) { 2231 return null; 2232 } 2233 if (!prefs.delegate.mEnabled) { 2234 return null; 2235 } 2236 return prefs.delegate.mPkg; 2237 } 2238 } 2239 2240 /** 2241 * Used by an app to delegate notification posting privileges to another apps. 2242 */ 2243 public void setNotificationDelegate(String sourcePkg, int sourceUid, 2244 String delegatePkg, int delegateUid) { 2245 synchronized (mLock) { 2246 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid); 2247 prefs.delegate = new Delegate(delegatePkg, delegateUid, true); 2248 } 2249 if (android.app.Flags.nmBinderPerfCacheChannels()) { 2250 // If package delegates change, then which packages can get what channel information 2251 // also changes, so we need to clear the cache. 2252 invalidateNotificationChannelCache(); 2253 } 2254 } 2255 2256 /** 2257 * Used by an app to turn off its notification delegate. 2258 */ 2259 public void revokeNotificationDelegate(String sourcePkg, int sourceUid) { 2260 synchronized (mLock) { 2261 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid); 2262 if (prefs != null && prefs.delegate != null) { 2263 prefs.delegate.mEnabled = false; 2264 } 2265 } 2266 if (android.app.Flags.nmBinderPerfCacheChannels()) { 2267 invalidateNotificationChannelCache(); 2268 } 2269 } 2270 2271 /** 2272 * Returns whether the given app is allowed on post notifications on behalf of the other given 2273 * app. 2274 */ 2275 public boolean isDelegateAllowed(String sourcePkg, int sourceUid, 2276 String potentialDelegatePkg, int potentialDelegateUid) { 2277 synchronized (mLock) { 2278 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid); 2279 2280 return prefs != null && prefs.isValidDelegate(potentialDelegatePkg, 2281 potentialDelegateUid); 2282 } 2283 } 2284 2285 private void lockFieldsForUpdateLocked(NotificationChannel original, 2286 NotificationChannel update) { 2287 if (original.canBypassDnd() != update.canBypassDnd()) { 2288 update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); 2289 } 2290 if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) { 2291 update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY); 2292 } 2293 if (original.getImportance() != update.getImportance()) { 2294 update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); 2295 } 2296 if (original.shouldShowLights() != update.shouldShowLights() 2297 || original.getLightColor() != update.getLightColor()) { 2298 update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS); 2299 } 2300 if (!Objects.equals(original.getSound(), update.getSound())) { 2301 update.lockFields(NotificationChannel.USER_LOCKED_SOUND); 2302 } 2303 if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern()) 2304 || !Objects.equals(original.getVibrationEffect(), update.getVibrationEffect()) 2305 || original.shouldVibrate() != update.shouldVibrate()) { 2306 update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION); 2307 } 2308 if (original.canShowBadge() != update.canShowBadge()) { 2309 update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE); 2310 } 2311 if (original.getAllowBubbles() != update.getAllowBubbles()) { 2312 update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE); 2313 } 2314 } 2315 2316 public void dump(PrintWriter pw, String prefix, 2317 @NonNull NotificationManagerService.DumpFilter filter, 2318 ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) { 2319 pw.print(prefix); 2320 pw.println("per-package config version: " + XML_VERSION); 2321 2322 pw.println("PackagePreferences:"); 2323 synchronized (mLock) { 2324 dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences, pkgPermissions); 2325 pw.println("Restored without uid:"); 2326 dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids, null); 2327 } 2328 } 2329 2330 public void dump(ProtoOutputStream proto, 2331 @NonNull NotificationManagerService.DumpFilter filter, 2332 ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) { 2333 synchronized (mLock) { 2334 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter, 2335 mPackagePreferences, pkgPermissions); 2336 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, 2337 filter, mRestoredWithoutUids, null); 2338 } 2339 } 2340 2341 @GuardedBy("mLock") 2342 private void dumpPackagePreferencesLocked(PrintWriter pw, String prefix, 2343 @NonNull NotificationManagerService.DumpFilter filter, 2344 ArrayMap<String, PackagePreferences> packagePreferences, 2345 ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> packagePermissions) { 2346 // Used for tracking which package preferences we've seen already for notification 2347 // permission reasons; after handling packages with local preferences, we'll want to dump 2348 // the ones with notification permissions set but not local prefs. 2349 Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null; 2350 if (packagePermissions != null) { 2351 pkgsWithPermissionsToHandle = packagePermissions.keySet(); 2352 } 2353 final int N = packagePreferences.size(); 2354 for (int i = 0; i < N; i++) { 2355 final PackagePreferences r = packagePreferences.valueAt(i); 2356 if (filter.matches(r.pkg)) { 2357 pw.print(prefix); 2358 pw.print(" AppSettings: "); 2359 pw.print(r.pkg); 2360 pw.print(" ("); 2361 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid)); 2362 pw.print(')'); 2363 Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); 2364 if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) { 2365 pw.print(" importance="); 2366 pw.print(NotificationListenerService.Ranking.importanceToString( 2367 packagePermissions.get(key).first 2368 ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE)); 2369 pw.print(" userSet="); 2370 pw.print(packagePermissions.get(key).second); 2371 pkgsWithPermissionsToHandle.remove(key); 2372 } 2373 if (r.priority != DEFAULT_PRIORITY) { 2374 pw.print(" priority="); 2375 pw.print(Notification.priorityToString(r.priority)); 2376 } 2377 if (r.visibility != DEFAULT_VISIBILITY) { 2378 pw.print(" visibility="); 2379 pw.print(Notification.visibilityToString(r.visibility)); 2380 } 2381 if (r.showBadge != DEFAULT_SHOW_BADGE) { 2382 pw.print(" showBadge="); 2383 pw.print(r.showBadge); 2384 } 2385 if (r.defaultAppLockedImportance != DEFAULT_APP_LOCKED_IMPORTANCE) { 2386 pw.print(" defaultAppLocked="); 2387 pw.print(r.defaultAppLockedImportance); 2388 } 2389 if (r.fixedImportance != DEFAULT_APP_LOCKED_IMPORTANCE) { 2390 pw.print(" fixedImportance="); 2391 pw.print(r.fixedImportance); 2392 } 2393 if (android.app.Flags.uiRichOngoing() 2394 && r.canHavePromotedNotifs != DEFAULT_CAN_HAVE_PROMOTED_NOTIFS) { 2395 pw.print(" promoted="); 2396 pw.print(r.canHavePromotedNotifs); 2397 } 2398 pw.println(); 2399 if (r.delegate != null) { 2400 pw.print(prefix); 2401 pw.printf(" Delegate: %s (%s) enabled=%s", r.delegate.mPkg, r.delegate.mUid, 2402 r.delegate.mEnabled); 2403 pw.println(); 2404 } 2405 for (NotificationChannel channel : r.channels.values()) { 2406 pw.print(prefix); 2407 channel.dump(pw, " ", filter.redact); 2408 } 2409 for (NotificationChannelGroup group : r.groups.values()) { 2410 pw.print(prefix); 2411 pw.print(" "); 2412 pw.print(" "); 2413 pw.println(group); 2414 } 2415 } 2416 } 2417 // Handle any remaining packages with permissions 2418 if (pkgsWithPermissionsToHandle != null) { 2419 for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) { 2420 // p.first is the uid of this package; p.second is the package name 2421 if (filter.matches(p.second)) { 2422 pw.print(prefix); 2423 pw.print(" AppSettings: "); 2424 pw.print(p.second); 2425 pw.print(" ("); 2426 pw.print(p.first == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(p.first)); 2427 pw.print(')'); 2428 pw.print(" importance="); 2429 pw.print(NotificationListenerService.Ranking.importanceToString( 2430 packagePermissions.get(p).first 2431 ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE)); 2432 pw.print(" userSet="); 2433 pw.print(packagePermissions.get(p).second); 2434 pw.println(); 2435 } 2436 } 2437 } 2438 } 2439 2440 private void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId, 2441 @NonNull NotificationManagerService.DumpFilter filter, 2442 ArrayMap<String, PackagePreferences> packagePreferences, 2443 ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> packagePermissions) { 2444 Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null; 2445 if (packagePermissions != null) { 2446 pkgsWithPermissionsToHandle = packagePermissions.keySet(); 2447 } 2448 2449 final int N = packagePreferences.size(); 2450 long fToken; 2451 for (int i = 0; i < N; i++) { 2452 final PackagePreferences r = packagePreferences.valueAt(i); 2453 if (filter.matches(r.pkg)) { 2454 fToken = proto.start(fieldId); 2455 2456 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg); 2457 proto.write(RankingHelperProto.RecordProto.UID, r.uid); 2458 Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); 2459 if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) { 2460 proto.write(RankingHelperProto.RecordProto.IMPORTANCE, 2461 packagePermissions.get(key).first 2462 ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE); 2463 pkgsWithPermissionsToHandle.remove(key); 2464 } 2465 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority); 2466 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility); 2467 proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge); 2468 2469 for (NotificationChannel channel : r.channels.values()) { 2470 channel.dumpDebug(proto, RankingHelperProto.RecordProto.CHANNELS); 2471 } 2472 for (NotificationChannelGroup group : r.groups.values()) { 2473 group.dumpDebug(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS); 2474 } 2475 2476 proto.end(fToken); 2477 } 2478 } 2479 2480 if (pkgsWithPermissionsToHandle != null) { 2481 for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) { 2482 if (filter.matches(p.second)) { 2483 fToken = proto.start(fieldId); 2484 proto.write(RankingHelperProto.RecordProto.PACKAGE, p.second); 2485 proto.write(RankingHelperProto.RecordProto.UID, p.first); 2486 proto.write(RankingHelperProto.RecordProto.IMPORTANCE, 2487 packagePermissions.get(p).first 2488 ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE); 2489 proto.end(fToken); 2490 } 2491 } 2492 } 2493 } 2494 2495 /** 2496 * @return State of the full screen intent permission for this package. 2497 */ 2498 @VisibleForTesting 2499 int getFsiState(String pkg, int uid, boolean requestedFSIPermission) { 2500 if (!requestedFSIPermission) { 2501 return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED; 2502 } 2503 final AttributionSource attributionSource = 2504 new AttributionSource.Builder(uid).setPackageName(pkg).build(); 2505 2506 final int result = mPermissionManager.checkPermissionForPreflight( 2507 android.Manifest.permission.USE_FULL_SCREEN_INTENT, attributionSource); 2508 2509 if (result == PermissionManager.PERMISSION_GRANTED) { 2510 return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED; 2511 } 2512 return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED; 2513 } 2514 2515 /** 2516 * @return True if the current full screen intent permission state for this package was set by 2517 * the user. 2518 */ 2519 @VisibleForTesting 2520 boolean isFsiPermissionUserSet(String pkg, int uid, int fsiState, int currentPermissionFlags) { 2521 if (fsiState == PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED) { 2522 return false; 2523 } 2524 return (currentPermissionFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; 2525 } 2526 2527 /** 2528 * Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}. 2529 */ 2530 public void pullPackagePreferencesStats(List<StatsEvent> events, 2531 ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) { 2532 pullPackagePreferencesStats(events, pkgPermissions, new ArrayMap<String, Set<Integer>>()); 2533 } 2534 2535 2536 /** 2537 * Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}. 2538 * @param events Newly filled out StatsEvent protos are added to this list as output. 2539 * @param pkgPermissions Maps from a pair representing a uid and package to a pair of booleans, 2540 * where the first represents whether the notification permission was 2541 * granted to that package, and the second represents whether the 2542 * permission was user-set. 2543 * @param pkgAdjustmentKeyTypes A map of package names that are not allowed to have their 2544 * notifications classified into differently typed notification 2545 * channels, and the channels that they're allowed to be 2546 * classified into. 2547 */ 2548 public void pullPackagePreferencesStats(List<StatsEvent> events, 2549 ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions, 2550 @NonNull Map<String, Set<Integer>> pkgAdjustmentKeyTypes) { 2551 Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null; 2552 if (pkgPermissions != null) { 2553 pkgsWithPermissionsToHandle = pkgPermissions.keySet(); 2554 } 2555 int pulledEvents = 0; 2556 synchronized (mLock) { 2557 for (int i = 0; i < mPackagePreferences.size(); i++) { 2558 if (pulledEvents > NOTIFICATION_PREFERENCES_PULL_LIMIT) { 2559 break; 2560 } 2561 pulledEvents++; 2562 final PackagePreferences r = mPackagePreferences.valueAt(i); 2563 2564 // collect whether this package's importance info was user-set for later, if needed 2565 // before the migration is enabled, this will simply default to false in all cases. 2566 boolean importanceIsUserSet = false; 2567 // Even if this package's data is not present, we need to write something; 2568 // default to IMPORTANCE_UNSPECIFIED. If PM doesn't know about the package 2569 // for some reason, notifications are not allowed, but in logged output we want 2570 // to distinguish this case from the actually-banned packages. 2571 int importance = IMPORTANCE_UNSPECIFIED; 2572 Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); 2573 if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) { 2574 Pair<Boolean, Boolean> permissionPair = pkgPermissions.get(key); 2575 importance = permissionPair.first 2576 ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE; 2577 // cache the second value for writing later 2578 importanceIsUserSet = permissionPair.second; 2579 2580 pkgsWithPermissionsToHandle.remove(key); 2581 } 2582 2583 final boolean requestedFSIPermission = mPermissionHelper.hasRequestedPermission( 2584 android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg, r.uid); 2585 2586 final int fsiState = getFsiState(r.pkg, r.uid, requestedFSIPermission); 2587 2588 final int currentPermissionFlags = mPm.getPermissionFlags( 2589 android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg, 2590 UserHandle.getUserHandleForUid(r.uid)); 2591 2592 final boolean fsiIsUserSet = 2593 isFsiPermissionUserSet(r.pkg, r.uid, fsiState, 2594 currentPermissionFlags); 2595 2596 if (!notificationClassificationUi() 2597 && pkgAdjustmentKeyTypes.keySet().size() > 0) { 2598 Slog.w(TAG, "Pkg adjustment types improperly allowed without flag set"); 2599 } 2600 2601 int[] allowedBundleTypes = 2602 getAllowedTypesForPackage(pkgAdjustmentKeyTypes, r.pkg); 2603 2604 events.add(FrameworkStatsLog.buildStatsEvent( 2605 PACKAGE_NOTIFICATION_PREFERENCES, 2606 /* optional int32 uid = 1 [(is_uid) = true] */ r.uid, 2607 /* optional int32 importance = 2 */ importance, 2608 /* optional int32 visibility = 3 */ r.visibility, 2609 /* optional int32 user_locked_fields = 4 */ r.lockedAppFields, 2610 /* optional bool user_set_importance = 5 */ importanceIsUserSet, 2611 /* optional FsiState fsi_state = 6 */ fsiState, 2612 /* optional bool is_fsi_permission_user_set = 7 */ fsiIsUserSet, 2613 /* repeated int32 allowed_bundle_types = 8 */ allowedBundleTypes 2614 )); 2615 } 2616 } 2617 2618 // handle remaining packages with PackageManager permissions but not local settings 2619 if (pkgPermissions != null) { 2620 for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) { 2621 if (pulledEvents > NOTIFICATION_PREFERENCES_PULL_LIMIT) { 2622 break; 2623 } 2624 pulledEvents++; 2625 2626 int[] allowedBundleTypes = 2627 getAllowedTypesForPackage(pkgAdjustmentKeyTypes, p.second); 2628 2629 // Because all fields are required in FrameworkStatsLog.buildStatsEvent, we have 2630 // to fill in default values for all the unspecified fields. 2631 events.add(FrameworkStatsLog.buildStatsEvent( 2632 PACKAGE_NOTIFICATION_PREFERENCES, 2633 /* optional int32 uid = 1 [(is_uid) = true] */ p.first, 2634 /* optional int32 importance = 2 */ pkgPermissions.get(p).first 2635 ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE, 2636 /* optional int32 visibility = 3 */ DEFAULT_VISIBILITY, 2637 /* optional int32 user_locked_fields = 4 */ DEFAULT_LOCKED_APP_FIELDS, 2638 /* optional bool user_set_importance = 5 */ pkgPermissions.get(p).second, 2639 /* optional FsiState fsi_state = 6 */ 0, 2640 /* optional bool is_fsi_permission_user_set = 7 */ false, 2641 /* repeated BundleTypes allowed_bundle_types = 8 */ allowedBundleTypes)); 2642 } 2643 } 2644 } 2645 2646 private int[] getAllowedTypesForPackage(@NonNull 2647 Map<String, Set<Integer>> pkgAdjustmentKeyTypes, 2648 String pkg) { 2649 int[] allowedBundleTypes = new int[]{}; 2650 if (notificationClassificationUi()) { 2651 if (pkgAdjustmentKeyTypes.containsKey(pkg)) { 2652 // Convert from set to int[] 2653 Set<Integer> types = pkgAdjustmentKeyTypes.get(pkg); 2654 allowedBundleTypes = new int[types.size()]; 2655 int i = 0; 2656 for (int val : types) { 2657 allowedBundleTypes[i] = val; 2658 i++; 2659 } 2660 } 2661 } 2662 return allowedBundleTypes; 2663 } 2664 2665 /** 2666 * Fills out {@link PackageNotificationChannelPreferences} proto and wraps it in a 2667 * {@link StatsEvent}. 2668 */ 2669 public void pullPackageChannelPreferencesStats(List<StatsEvent> events) { 2670 synchronized (mLock) { 2671 int totalChannelsPulled = 0; 2672 for (int i = 0; i < mPackagePreferences.size(); i++) { 2673 if (totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) { 2674 break; 2675 } 2676 final PackagePreferences r = mPackagePreferences.valueAt(i); 2677 for (NotificationChannel channel : r.channels.values()) { 2678 if (++totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) { 2679 break; 2680 } 2681 events.add(FrameworkStatsLog.buildStatsEvent( 2682 PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES, 2683 /* optional int32 uid = 1 [(is_uid) = true] */ r.uid, 2684 /* optional string channel_id = 2 */ channel.getId(), 2685 /* optional string channel_name = 3 */ channel.getName().toString(), 2686 /* optional string description = 4 */ channel.getDescription(), 2687 /* optional int32 importance = 5 */ channel.getImportance(), 2688 /* optional int32 user_locked_fields = 6 */ 2689 channel.getUserLockedFields(), 2690 /* optional bool is_deleted = 7 */ channel.isDeleted(), 2691 /* optional bool is_conversation = 8 */ 2692 channel.getConversationId() != null, 2693 /* optional bool is_demoted_conversation = 9 */ channel.isDemoted(), 2694 /* optional bool is_important_conversation = 10 */ 2695 channel.isImportantConversation())); 2696 } 2697 } 2698 } 2699 } 2700 2701 /** 2702 * Fills out {@link PackageNotificationChannelGroupPreferences} proto and wraps it in a 2703 * {@link StatsEvent}. 2704 */ 2705 public void pullPackageChannelGroupPreferencesStats(List<StatsEvent> events) { 2706 synchronized (mLock) { 2707 int totalGroupsPulled = 0; 2708 for (int i = 0; i < mPackagePreferences.size(); i++) { 2709 if (totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) { 2710 break; 2711 } 2712 final PackagePreferences r = mPackagePreferences.valueAt(i); 2713 for (NotificationChannelGroup groupChannel : r.groups.values()) { 2714 if (++totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) { 2715 break; 2716 } 2717 events.add(FrameworkStatsLog.buildStatsEvent( 2718 PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES, 2719 /* optional int32 uid = 1 [(is_uid) = true] */ r.uid, 2720 /* optional string group_id = 2 */ groupChannel.getId(), 2721 /* optional string group_name = 3 */ groupChannel.getName().toString(), 2722 /* optional string description = 4 */ groupChannel.getDescription(), 2723 /* optional bool is_blocked = 5 */ groupChannel.isBlocked(), 2724 /* optional int32 user_locked_fields = 6 */ 2725 groupChannel.getUserLockedFields())); 2726 } 2727 } 2728 } 2729 } 2730 2731 public JSONObject dumpJson(NotificationManagerService.DumpFilter filter, 2732 ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) { 2733 JSONObject ranking = new JSONObject(); 2734 JSONArray PackagePreferencess = new JSONArray(); 2735 synchronized (mLock) { 2736 try { 2737 ranking.put("noUid", mRestoredWithoutUids.size()); 2738 } catch (JSONException e) { 2739 // pass 2740 } 2741 } 2742 2743 // Track data that we've handled from the permissions-based list 2744 Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null; 2745 if (pkgPermissions != null) { 2746 pkgsWithPermissionsToHandle = pkgPermissions.keySet(); 2747 } 2748 2749 synchronized (mLock) { 2750 final int N = mPackagePreferences.size(); 2751 for (int i = 0; i < N; i++) { 2752 final PackagePreferences r = mPackagePreferences.valueAt(i); 2753 if (filter == null || filter.matches(r.pkg)) { 2754 JSONObject PackagePreferences = new JSONObject(); 2755 try { 2756 PackagePreferences.put("userId", UserHandle.getUserId(r.uid)); 2757 PackagePreferences.put("packageName", r.pkg); 2758 Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); 2759 if (pkgPermissions != null 2760 && pkgsWithPermissionsToHandle.contains(key)) { 2761 PackagePreferences.put("importance", 2762 NotificationListenerService.Ranking.importanceToString( 2763 pkgPermissions.get(key).first 2764 ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE)); 2765 pkgsWithPermissionsToHandle.remove(key); 2766 } 2767 if (r.priority != DEFAULT_PRIORITY) { 2768 PackagePreferences.put("priority", 2769 Notification.priorityToString(r.priority)); 2770 } 2771 if (r.visibility != DEFAULT_VISIBILITY) { 2772 PackagePreferences.put("visibility", 2773 Notification.visibilityToString(r.visibility)); 2774 } 2775 if (r.showBadge != DEFAULT_SHOW_BADGE) { 2776 PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge)); 2777 } 2778 JSONArray channels = new JSONArray(); 2779 for (NotificationChannel channel : r.channels.values()) { 2780 channels.put(channel.toJson()); 2781 } 2782 PackagePreferences.put("channels", channels); 2783 JSONArray groups = new JSONArray(); 2784 for (NotificationChannelGroup group : r.groups.values()) { 2785 groups.put(group.toJson()); 2786 } 2787 PackagePreferences.put("groups", groups); 2788 } catch (JSONException e) { 2789 // pass 2790 } 2791 PackagePreferencess.put(PackagePreferences); 2792 } 2793 } 2794 } 2795 2796 // handle packages for which there are permissions but no local settings 2797 if (pkgsWithPermissionsToHandle != null) { 2798 for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) { 2799 if (filter == null || filter.matches(p.second)) { 2800 JSONObject PackagePreferences = new JSONObject(); 2801 try { 2802 PackagePreferences.put("userId", UserHandle.getUserId(p.first)); 2803 PackagePreferences.put("packageName", p.second); 2804 PackagePreferences.put("importance", 2805 NotificationListenerService.Ranking.importanceToString( 2806 pkgPermissions.get(p).first 2807 ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE)); 2808 } catch (JSONException e) { 2809 // pass 2810 } 2811 PackagePreferencess.put(PackagePreferences); 2812 } 2813 } 2814 } 2815 2816 try { 2817 ranking.put("PackagePreferencess", PackagePreferencess); 2818 } catch (JSONException e) { 2819 // pass 2820 } 2821 return ranking; 2822 } 2823 2824 /** 2825 * Dump only the ban information as structured JSON for the stats collector. 2826 * 2827 * This is intentionally redundant with {#link dumpJson} because the old 2828 * scraper will expect this format. 2829 * 2830 * @param filter 2831 * @return 2832 */ 2833 public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter, 2834 ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) { 2835 JSONArray bans = new JSONArray(); 2836 Map<Integer, String> packageBans = getPermissionBasedPackageBans(pkgPermissions); 2837 for (Map.Entry<Integer, String> ban : packageBans.entrySet()) { 2838 final int userId = UserHandle.getUserId(ban.getKey()); 2839 final String packageName = ban.getValue(); 2840 if (filter == null || filter.matches(packageName)) { 2841 JSONObject banJson = new JSONObject(); 2842 try { 2843 banJson.put("userId", userId); 2844 banJson.put("packageName", packageName); 2845 } catch (JSONException e) { 2846 e.printStackTrace(); 2847 } 2848 bans.put(banJson); 2849 } 2850 } 2851 return bans; 2852 } 2853 2854 public Map<Integer, String> getPackageBans() { 2855 synchronized (mLock) { 2856 final int N = mPackagePreferences.size(); 2857 ArrayMap<Integer, String> packageBans = new ArrayMap<>(N); 2858 for (int i = 0; i < N; i++) { 2859 final PackagePreferences r = mPackagePreferences.valueAt(i); 2860 if (r.importance == IMPORTANCE_NONE) { 2861 packageBans.put(r.uid, r.pkg); 2862 } 2863 } 2864 2865 return packageBans; 2866 } 2867 } 2868 2869 // Same functionality as getPackageBans by extracting the set of packages from the provided 2870 // map that are disallowed from sending notifications. 2871 protected Map<Integer, String> getPermissionBasedPackageBans( 2872 ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) { 2873 ArrayMap<Integer, String> packageBans = new ArrayMap<>(); 2874 if (pkgPermissions != null) { 2875 for (Pair<Integer, String> p : pkgPermissions.keySet()) { 2876 if (!pkgPermissions.get(p).first) { 2877 packageBans.put(p.first, p.second); 2878 } 2879 } 2880 } 2881 return packageBans; 2882 } 2883 2884 /** 2885 * Dump only the channel information as structured JSON for the stats collector. 2886 * 2887 * This is intentionally redundant with {#link dumpJson} because the old 2888 * scraper will expect this format. 2889 * 2890 * @param filter 2891 * @return 2892 */ 2893 public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) { 2894 JSONArray channels = new JSONArray(); 2895 Map<String, Integer> packageChannels = getPackageChannels(); 2896 for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) { 2897 final String packageName = channelCount.getKey(); 2898 if (filter == null || filter.matches(packageName)) { 2899 JSONObject channelCountJson = new JSONObject(); 2900 try { 2901 channelCountJson.put("packageName", packageName); 2902 channelCountJson.put("channelCount", channelCount.getValue()); 2903 } catch (JSONException e) { 2904 e.printStackTrace(); 2905 } 2906 channels.put(channelCountJson); 2907 } 2908 } 2909 return channels; 2910 } 2911 2912 private Map<String, Integer> getPackageChannels() { 2913 ArrayMap<String, Integer> packageChannels = new ArrayMap<>(); 2914 synchronized (mLock) { 2915 for (int i = 0; i < mPackagePreferences.size(); i++) { 2916 final PackagePreferences r = mPackagePreferences.valueAt(i); 2917 int channelCount = 0; 2918 for (int j = 0; j < r.channels.size(); j++) { 2919 if (!r.channels.valueAt(j).isDeleted()) { 2920 channelCount++; 2921 } 2922 } 2923 packageChannels.put(r.pkg, channelCount); 2924 } 2925 } 2926 return packageChannels; 2927 } 2928 2929 public void onUserRemoved(int userId) { 2930 synchronized (mLock) { 2931 boolean removed = false; 2932 int N = mPackagePreferences.size(); 2933 for (int i = N - 1; i >= 0; i--) { 2934 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i); 2935 if (UserHandle.getUserId(PackagePreferences.uid) == userId) { 2936 mPackagePreferences.removeAt(i); 2937 removed = true; 2938 } 2939 } 2940 if (android.app.Flags.nmBinderPerfCacheChannels() && removed) { 2941 invalidateNotificationChannelCache(); 2942 invalidateNotificationChannelGroupCache(); 2943 } 2944 } 2945 } 2946 2947 protected void onLocaleChanged(Context context, int userId) { 2948 synchronized (mLock) { 2949 boolean updated = false; 2950 int N = mPackagePreferences.size(); 2951 for (int i = 0; i < N; i++) { 2952 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i); 2953 if (UserHandle.getUserId(PackagePreferences.uid) == userId) { 2954 if (PackagePreferences.channels.containsKey( 2955 DEFAULT_CHANNEL_ID)) { 2956 PackagePreferences.channels.get( 2957 DEFAULT_CHANNEL_ID).setName( 2958 context.getResources().getString( 2959 R.string.default_notification_channel_label)); 2960 updated = true; 2961 } 2962 // TODO (b/346396459): Localize all reserved channels 2963 } 2964 } 2965 if (android.app.Flags.nmBinderPerfCacheChannels() && updated) { 2966 invalidateNotificationChannelCache(); 2967 } 2968 } 2969 } 2970 2971 public boolean onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList, 2972 int[] uidList) { 2973 if (pkgList == null || pkgList.length == 0) { 2974 return false; // nothing to do 2975 } 2976 boolean updated = false; 2977 if (removingPackage) { 2978 // Remove notification settings for uninstalled package 2979 int size = Math.min(pkgList.length, uidList.length); 2980 for (int i = 0; i < size; i++) { 2981 final String pkg = pkgList[i]; 2982 final int uid = uidList[i]; 2983 synchronized (mLock) { 2984 mPackagePreferences.remove(packagePreferencesKey(pkg, uid)); 2985 mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId)); 2986 } 2987 updated = true; 2988 } 2989 } else { 2990 for (String pkg : pkgList) { 2991 try { 2992 // Package install 2993 int uid = mPm.getPackageUidAsUser(pkg, changeUserId); 2994 PackagePermission p = null; 2995 synchronized (mLock) { 2996 final PackagePreferences r = 2997 mRestoredWithoutUids.get(unrestoredPackageKey(pkg, changeUserId)); 2998 if (r != null) { 2999 r.uid = uid; 3000 mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId)); 3001 mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r); 3002 3003 // Try to restore any unrestored sound resources 3004 for (NotificationChannel channel : r.channels.values()) { 3005 if (!channel.isSoundRestored()) { 3006 Uri uri = channel.getSound(); 3007 Uri restoredUri = 3008 channel.restoreSoundUri( 3009 mContext, 3010 uri, 3011 true, 3012 channel.getAudioAttributes().getUsage()); 3013 if (Settings.System.DEFAULT_NOTIFICATION_URI.equals( 3014 restoredUri)) { 3015 Slog.w(TAG, 3016 "Could not restore sound: " + uri + " for channel: " 3017 + channel); 3018 } 3019 channel.setSound(restoredUri, channel.getAudioAttributes()); 3020 } 3021 } 3022 3023 if (r.migrateToPm) { 3024 p = new PackagePermission( 3025 r.pkg, UserHandle.getUserId(r.uid), 3026 r.importance != IMPORTANCE_NONE, 3027 hasUserConfiguredSettings(r)); 3028 } 3029 updated = true; 3030 } 3031 } 3032 if (p != null) { 3033 mPermissionHelper.setNotificationPermission(p); 3034 } 3035 } catch (Exception e) { 3036 Slog.e(TAG, "could not restore " + pkg, e); 3037 } 3038 // Package upgrade 3039 try { 3040 PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg, 3041 mPm.getPackageUidAsUser(pkg, changeUserId)); 3042 if (fullPackagePreferences != null) { 3043 updated |= createDefaultChannelIfNeededLocked(fullPackagePreferences); 3044 updated |= deleteDefaultChannelIfNeededLocked(fullPackagePreferences); 3045 } 3046 } catch (PackageManager.NameNotFoundException e) { 3047 } 3048 } 3049 } 3050 3051 if (updated) { 3052 updateConfig(); 3053 if (android.app.Flags.nmBinderPerfCacheChannels()) { 3054 invalidateNotificationChannelCache(); 3055 invalidateNotificationChannelGroupCache(); 3056 } 3057 } 3058 return updated; 3059 } 3060 3061 public void clearData(String pkg, int uid) { 3062 synchronized (mLock) { 3063 PackagePreferences p = getPackagePreferencesLocked(pkg, uid); 3064 if (p != null) { 3065 p.channels = new ArrayMap<>(); 3066 p.groups = new ArrayMap<>(); 3067 p.delegate = null; 3068 p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS; 3069 p.bubblePreference = DEFAULT_BUBBLE_PREFERENCE; 3070 p.importance = DEFAULT_IMPORTANCE; 3071 p.priority = DEFAULT_PRIORITY; 3072 p.visibility = DEFAULT_VISIBILITY; 3073 p.showBadge = DEFAULT_SHOW_BADGE; 3074 if (android.app.Flags.nmBinderPerfCacheChannels()) { 3075 invalidateNotificationChannelCache(); 3076 invalidateNotificationChannelGroupCache(); 3077 } 3078 } 3079 } 3080 } 3081 3082 private LogMaker getChannelLog(NotificationChannel channel, String pkg) { 3083 return new LogMaker( 3084 com.android.internal.logging.nano.MetricsProto.MetricsEvent 3085 .ACTION_NOTIFICATION_CHANNEL) 3086 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE) 3087 .setPackageName(pkg) 3088 .addTaggedData( 3089 com.android.internal.logging.nano.MetricsProto.MetricsEvent 3090 .FIELD_NOTIFICATION_CHANNEL_ID, 3091 channel.getId()) 3092 .addTaggedData( 3093 com.android.internal.logging.nano.MetricsProto.MetricsEvent 3094 .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, 3095 channel.getImportance()); 3096 } 3097 3098 private LogMaker getChannelGroupLog(String groupId, String pkg) { 3099 return new LogMaker( 3100 com.android.internal.logging.nano.MetricsProto.MetricsEvent 3101 .ACTION_NOTIFICATION_CHANNEL_GROUP) 3102 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE) 3103 .addTaggedData( 3104 com.android.internal.logging.nano.MetricsProto.MetricsEvent 3105 .FIELD_NOTIFICATION_CHANNEL_GROUP_ID, 3106 groupId) 3107 .setPackageName(pkg); 3108 } 3109 3110 /** Requests check of the feature setting for showing media notifications in quick settings. */ 3111 public void updateMediaNotificationFilteringEnabled() { 3112 // TODO(b/192412820): Consolidate SHOW_MEDIA_ON_QUICK_SETTINGS into compile-time value. 3113 final boolean newValue = Settings.Global.getInt(mContext.getContentResolver(), 3114 Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1) > 0 3115 && mContext.getResources().getBoolean( 3116 R.bool.config_quickSettingsShowMediaPlayer); 3117 if (newValue != mIsMediaNotificationFilteringEnabled) { 3118 mIsMediaNotificationFilteringEnabled = newValue; 3119 updateConfig(); 3120 } 3121 } 3122 3123 /** Returns true if the setting is enabled for showing media notifications in quick settings. */ 3124 public boolean isMediaNotificationFilteringEnabled() { 3125 return mIsMediaNotificationFilteringEnabled; 3126 } 3127 3128 public void updateBadgingEnabled() { 3129 if (mBadgingEnabled == null) { 3130 mBadgingEnabled = new SparseBooleanArray(); 3131 } 3132 boolean changed = false; 3133 // update the cached values 3134 for (int index = 0; index < mBadgingEnabled.size(); index++) { 3135 int userId = mBadgingEnabled.keyAt(index); 3136 final boolean oldValue = mBadgingEnabled.get(userId); 3137 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(), 3138 Settings.Secure.NOTIFICATION_BADGING, 3139 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0; 3140 mBadgingEnabled.put(userId, newValue); 3141 changed |= oldValue != newValue; 3142 } 3143 if (changed) { 3144 updateConfig(); 3145 } 3146 } 3147 3148 public boolean badgingEnabled(UserHandle userHandle) { 3149 int userId = userHandle.getIdentifier(); 3150 if (userId == UserHandle.USER_ALL) { 3151 return false; 3152 } 3153 if (mBadgingEnabled.indexOfKey(userId) < 0) { 3154 mBadgingEnabled.put(userId, 3155 Settings.Secure.getIntForUser(mContext.getContentResolver(), 3156 Settings.Secure.NOTIFICATION_BADGING, 3157 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0); 3158 } 3159 return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE); 3160 } 3161 3162 /** Updates whether bubbles are enabled for this user. */ 3163 public void updateBubblesEnabled() { 3164 if (mBubblesEnabled == null) { 3165 mBubblesEnabled = new SparseBooleanArray(); 3166 } 3167 boolean changed = false; 3168 // update the cached values 3169 for (int index = 0; index < mBubblesEnabled.size(); index++) { 3170 int userId = mBubblesEnabled.keyAt(index); 3171 final boolean oldValue = mBubblesEnabled.get(userId); 3172 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(), 3173 Settings.Secure.NOTIFICATION_BUBBLES, 3174 DEFAULT_BUBBLES_ENABLED ? 1 : 0, userId) != 0; 3175 mBubblesEnabled.put(userId, newValue); 3176 changed |= oldValue != newValue; 3177 } 3178 if (changed) { 3179 updateConfig(); 3180 } 3181 } 3182 3183 /** Returns true if bubbles are enabled for this user. */ 3184 public boolean bubblesEnabled(UserHandle userHandle) { 3185 int userId = userHandle.getIdentifier(); 3186 if (userId == UserHandle.USER_ALL) { 3187 return false; 3188 } 3189 if (mBubblesEnabled.indexOfKey(userId) < 0) { 3190 mBubblesEnabled.put(userId, 3191 Settings.Secure.getIntForUser(mContext.getContentResolver(), 3192 Settings.Secure.NOTIFICATION_BUBBLES, 3193 DEFAULT_BUBBLES_ENABLED ? 1 : 0, userId) != 0); 3194 } 3195 return mBubblesEnabled.get(userId, DEFAULT_BUBBLES_ENABLED); 3196 } 3197 3198 public void updateLockScreenPrivateNotifications() { 3199 if (mLockScreenPrivateNotifications == null) { 3200 mLockScreenPrivateNotifications = new SparseBooleanArray(); 3201 } 3202 boolean changed = false; 3203 // update the cached values 3204 for (int index = 0; index < mLockScreenPrivateNotifications.size(); index++) { 3205 int userId = mLockScreenPrivateNotifications.keyAt(index); 3206 final boolean oldValue = mLockScreenPrivateNotifications.get(userId); 3207 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(), 3208 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, userId) != 0; 3209 mLockScreenPrivateNotifications.put(userId, newValue); 3210 changed |= oldValue != newValue; 3211 } 3212 if (changed) { 3213 updateConfig(); 3214 } 3215 } 3216 3217 public void updateLockScreenShowNotifications() { 3218 if (mLockScreenShowNotifications == null) { 3219 mLockScreenShowNotifications = new SparseBooleanArray(); 3220 } 3221 boolean changed = false; 3222 // update the cached values 3223 for (int index = 0; index < mLockScreenShowNotifications.size(); index++) { 3224 int userId = mLockScreenShowNotifications.keyAt(index); 3225 final boolean oldValue = mLockScreenShowNotifications.get(userId); 3226 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(), 3227 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, userId) != 0; 3228 mLockScreenShowNotifications.put(userId, newValue); 3229 changed |= oldValue != newValue; 3230 } 3231 if (changed) { 3232 updateConfig(); 3233 } 3234 } 3235 3236 @Override 3237 public boolean canShowNotificationsOnLockscreen(int userId) { 3238 if (mLockScreenShowNotifications == null) { 3239 mLockScreenShowNotifications = new SparseBooleanArray(); 3240 } 3241 return mLockScreenShowNotifications.get(userId, true); 3242 } 3243 3244 @Override 3245 public boolean canShowPrivateNotificationsOnLockScreen(int userId) { 3246 if (mLockScreenPrivateNotifications == null) { 3247 mLockScreenPrivateNotifications = new SparseBooleanArray(); 3248 } 3249 return mLockScreenPrivateNotifications.get(userId, true); 3250 } 3251 3252 public void unlockAllNotificationChannels() { 3253 synchronized (mLock) { 3254 final int numPackagePreferences = mPackagePreferences.size(); 3255 for (int i = 0; i < numPackagePreferences; i++) { 3256 final PackagePreferences r = mPackagePreferences.valueAt(i); 3257 for (NotificationChannel channel : r.channels.values()) { 3258 channel.unlockFields(USER_LOCKED_IMPORTANCE); 3259 } 3260 } 3261 } 3262 if (android.app.Flags.nmBinderPerfCacheChannels()) { 3263 invalidateNotificationChannelCache(); 3264 } 3265 } 3266 3267 public void migrateNotificationPermissions(List<UserInfo> users) { 3268 for (UserInfo user : users) { 3269 List<PackageInfo> packages = mPm.getInstalledPackagesAsUser( 3270 PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL), 3271 user.getUserHandle().getIdentifier()); 3272 for (PackageInfo pi : packages) { 3273 synchronized (mLock) { 3274 PackagePreferences p = getOrCreatePackagePreferencesLocked( 3275 pi.packageName, pi.applicationInfo.uid); 3276 if (p.migrateToPm && p.uid != UNKNOWN_UID) { 3277 try { 3278 PackagePermission pkgPerm = new PackagePermission( 3279 p.pkg, UserHandle.getUserId(p.uid), 3280 p.importance != IMPORTANCE_NONE, 3281 hasUserConfiguredSettings(p)); 3282 mPermissionHelper.setNotificationPermission(pkgPerm); 3283 } catch (Exception e) { 3284 Slog.e(TAG, "could not migrate setting for " + p.pkg, e); 3285 } 3286 } 3287 } 3288 } 3289 } 3290 } 3291 3292 private void updateConfig() { 3293 mRankingHandler.requestSort(); 3294 } 3295 3296 @VisibleForTesting 3297 // Utility method for overriding in tests to confirm that the cache gets cleared. 3298 protected void invalidateNotificationChannelCache() { 3299 NotificationManager.invalidateNotificationChannelCache(); 3300 } 3301 3302 @VisibleForTesting 3303 protected void invalidateNotificationChannelGroupCache() { 3304 NotificationManager.invalidateNotificationChannelGroupCache(); 3305 } 3306 3307 private static String packagePreferencesKey(String pkg, int uid) { 3308 return pkg + "|" + uid; 3309 } 3310 3311 private static String unrestoredPackageKey(String pkg, @UserIdInt int userId) { 3312 return pkg + "|" + userId; 3313 } 3314 3315 private static class PackagePreferences { 3316 String pkg; 3317 int uid = UNKNOWN_UID; 3318 int importance = DEFAULT_IMPORTANCE; 3319 int priority = DEFAULT_PRIORITY; 3320 int visibility = DEFAULT_VISIBILITY; 3321 boolean showBadge = DEFAULT_SHOW_BADGE; 3322 int bubblePreference = DEFAULT_BUBBLE_PREFERENCE; 3323 int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS; 3324 // these fields are loaded on boot from a different source of truth and so are not 3325 // written to notification policy xml 3326 boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE; 3327 boolean fixedImportance = DEFAULT_APP_LOCKED_IMPORTANCE; 3328 3329 boolean hasSentInvalidMessage = false; 3330 boolean hasSentValidMessage = false; 3331 // note: only valid while hasSentMessage is false and hasSentInvalidMessage is true 3332 boolean userDemotedMsgApp = false; 3333 boolean hasSentValidBubble = false; 3334 3335 boolean migrateToPm = false; 3336 long creationTime; 3337 3338 @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING) 3339 // Until we enable the UI, we should return false. 3340 boolean canHavePromotedNotifs = android.app.Flags.uiRichOngoing(); 3341 3342 @UserIdInt int userIdWhenUidUnknown; 3343 3344 Delegate delegate = null; 3345 ArrayMap<String, NotificationChannel> channels = new ArrayMap<>(); 3346 Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>(); 3347 3348 public boolean isValidDelegate(String pkg, int uid) { 3349 return delegate != null && delegate.isAllowed(pkg, uid); 3350 } 3351 } 3352 3353 private static class Delegate { 3354 static final boolean DEFAULT_ENABLED = true; 3355 3356 final String mPkg; 3357 final int mUid; 3358 boolean mEnabled; 3359 3360 Delegate(String pkg, int uid, boolean enabled) { 3361 mPkg = pkg; 3362 mUid = uid; 3363 mEnabled = enabled; 3364 } 3365 3366 public boolean isAllowed(String pkg, int uid) { 3367 if (pkg == null || uid == UNKNOWN_UID) { 3368 return false; 3369 } 3370 return pkg.equals(mPkg) 3371 && uid == mUid 3372 && mEnabled; 3373 } 3374 } 3375 } 3376