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