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