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