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