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