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