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