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