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