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