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