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