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.NotificationManager.IMPORTANCE_NONE;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.app.Notification;
25 import android.app.NotificationChannel;
26 import android.app.NotificationChannelGroup;
27 import android.app.NotificationManager;
28 import android.content.Context;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.ParceledListSlice;
32 import android.metrics.LogMaker;
33 import android.os.Build;
34 import android.os.UserHandle;
35 import android.provider.Settings;
36 import android.service.notification.NotificationListenerService;
37 import android.service.notification.RankingHelperProto;
38 import android.text.TextUtils;
39 import android.util.ArrayMap;
40 import android.util.ArraySet;
41 import android.util.Pair;
42 import android.util.Slog;
43 import android.util.SparseBooleanArray;
44 import android.util.proto.ProtoOutputStream;
45 
46 import com.android.internal.R;
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.internal.logging.MetricsLogger;
49 import com.android.internal.util.Preconditions;
50 import com.android.internal.util.XmlUtils;
51 
52 import org.json.JSONArray;
53 import org.json.JSONException;
54 import org.json.JSONObject;
55 import org.xmlpull.v1.XmlPullParser;
56 import org.xmlpull.v1.XmlPullParserException;
57 import org.xmlpull.v1.XmlSerializer;
58 
59 import java.io.IOException;
60 import java.io.PrintWriter;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.Collection;
64 import java.util.List;
65 import java.util.Map;
66 import java.util.Objects;
67 import java.util.concurrent.ConcurrentHashMap;
68 
69 public class PreferencesHelper implements RankingConfig {
70     private static final String TAG = "NotificationPrefHelper";
71     private static final int XML_VERSION = 1;
72     private static final int UNKNOWN_UID = UserHandle.USER_NULL;
73     private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
74 
75     @VisibleForTesting
76     static final String TAG_RANKING = "ranking";
77     private static final String TAG_PACKAGE = "package";
78     private static final String TAG_CHANNEL = "channel";
79     private static final String TAG_GROUP = "channelGroup";
80     private static final String TAG_DELEGATE = "delegate";
81     private static final String TAG_STATUS_ICONS = "silent_status_icons";
82 
83     private static final String ATT_VERSION = "version";
84     private static final String ATT_NAME = "name";
85     private static final String ATT_UID = "uid";
86     private static final String ATT_ID = "id";
87     private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
88     private static final String ATT_PRIORITY = "priority";
89     private static final String ATT_VISIBILITY = "visibility";
90     private static final String ATT_IMPORTANCE = "importance";
91     private static final String ATT_SHOW_BADGE = "show_badge";
92     private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
93     private static final String ATT_ENABLED = "enabled";
94     private static final String ATT_USER_ALLOWED = "allowed";
95     private static final String ATT_HIDE_SILENT = "hide_gentle";
96 
97     private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
98     private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
99     private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
100     @VisibleForTesting
101     static final boolean DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS = false;
102     private static final boolean DEFAULT_SHOW_BADGE = true;
103     private static final boolean DEFAULT_ALLOW_BUBBLE = true;
104     private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE  = false;
105     private static final boolean DEFAULT_APP_LOCKED_IMPORTANCE  = false;
106 
107     /**
108      * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
109      * fields.
110      */
111     private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
112 
113     /**
114      * All user-lockable fields for a given application.
115      */
116     @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
117     public @interface LockableAppFields {
118         int USER_LOCKED_IMPORTANCE = 0x00000001;
119         int USER_LOCKED_BUBBLE = 0x00000002;
120     }
121 
122     // pkg|uid => PackagePreferences
123     private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>();
124     // pkg => PackagePreferences
125     private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
126 
127     private final Context mContext;
128     private final PackageManager mPm;
129     private final RankingHandler mRankingHandler;
130     private final ZenModeHelper mZenModeHelper;
131 
132     private SparseBooleanArray mBadgingEnabled;
133     private SparseBooleanArray mBubblesEnabled;
134     private boolean mAreChannelsBypassingDnd;
135     private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
136 
PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, ZenModeHelper zenHelper)137     public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
138             ZenModeHelper zenHelper) {
139         mContext = context;
140         mZenModeHelper = zenHelper;
141         mRankingHandler = rankingHandler;
142         mPm = pm;
143 
144         updateBadgingEnabled();
145         updateBubblesEnabled();
146         syncChannelsBypassingDnd(mContext.getUserId());
147     }
148 
readXml(XmlPullParser parser, boolean forRestore, int userId)149     public void readXml(XmlPullParser parser, boolean forRestore, int userId)
150             throws XmlPullParserException, IOException {
151         int type = parser.getEventType();
152         if (type != XmlPullParser.START_TAG) return;
153         String tag = parser.getName();
154         if (!TAG_RANKING.equals(tag)) return;
155         synchronized (mPackagePreferences) {
156             // Clobber groups and channels with the xml, but don't delete other data that wasn't
157             // present at the time of serialization.
158             mRestoredWithoutUids.clear();
159             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
160                 tag = parser.getName();
161                 if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
162                     return;
163                 }
164                 if (type == XmlPullParser.START_TAG) {
165                     if (TAG_STATUS_ICONS.equals(tag)) {
166                         if (forRestore && userId != UserHandle.USER_SYSTEM) {
167                             continue;
168                         }
169                         mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute(
170                                 parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
171                     } else if (TAG_PACKAGE.equals(tag)) {
172                         int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
173                         String name = parser.getAttributeValue(null, ATT_NAME);
174                         if (!TextUtils.isEmpty(name)) {
175                             if (forRestore) {
176                                 try {
177                                     uid = mPm.getPackageUidAsUser(name, userId);
178                                 } catch (PackageManager.NameNotFoundException e) {
179                                     // noop
180                                 }
181                             }
182 
183                             PackagePreferences r = getOrCreatePackagePreferencesLocked(name, uid,
184                                     XmlUtils.readIntAttribute(
185                                             parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
186                                     XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
187                                             DEFAULT_PRIORITY),
188                                     XmlUtils.readIntAttribute(
189                                             parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
190                                     XmlUtils.readBooleanAttribute(
191                                             parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
192                                     XmlUtils.readBooleanAttribute(
193                                             parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
194                             r.importance = XmlUtils.readIntAttribute(
195                                     parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
196                             r.priority = XmlUtils.readIntAttribute(
197                                     parser, ATT_PRIORITY, DEFAULT_PRIORITY);
198                             r.visibility = XmlUtils.readIntAttribute(
199                                     parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
200                             r.showBadge = XmlUtils.readBooleanAttribute(
201                                     parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
202                             r.lockedAppFields = XmlUtils.readIntAttribute(parser,
203                                     ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
204 
205                             final int innerDepth = parser.getDepth();
206                             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
207                                     && (type != XmlPullParser.END_TAG
208                                     || parser.getDepth() > innerDepth)) {
209                                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
210                                     continue;
211                                 }
212 
213                                 String tagName = parser.getName();
214                                 // Channel groups
215                                 if (TAG_GROUP.equals(tagName)) {
216                                     String id = parser.getAttributeValue(null, ATT_ID);
217                                     CharSequence groupName = parser.getAttributeValue(null,
218                                             ATT_NAME);
219                                     if (!TextUtils.isEmpty(id)) {
220                                         NotificationChannelGroup group
221                                                 = new NotificationChannelGroup(id, groupName);
222                                         group.populateFromXml(parser);
223                                         r.groups.put(id, group);
224                                     }
225                                 }
226                                 // Channels
227                                 if (TAG_CHANNEL.equals(tagName)) {
228                                     String id = parser.getAttributeValue(null, ATT_ID);
229                                     String channelName = parser.getAttributeValue(null, ATT_NAME);
230                                     int channelImportance = XmlUtils.readIntAttribute(
231                                             parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
232                                     if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
233                                         NotificationChannel channel = new NotificationChannel(id,
234                                                 channelName, channelImportance);
235                                         if (forRestore) {
236                                             channel.populateFromXmlForRestore(parser, mContext);
237                                         } else {
238                                             channel.populateFromXml(parser);
239                                         }
240                                         channel.setImportanceLockedByCriticalDeviceFunction(
241                                                 r.defaultAppLockedImportance);
242                                         r.channels.put(id, channel);
243                                     }
244                                 }
245                                 // Delegate
246                                 if (TAG_DELEGATE.equals(tagName)) {
247                                     int delegateId =
248                                             XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
249                                     String delegateName =
250                                             XmlUtils.readStringAttribute(parser, ATT_NAME);
251                                     boolean delegateEnabled = XmlUtils.readBooleanAttribute(
252                                             parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
253                                     boolean userAllowed = XmlUtils.readBooleanAttribute(
254                                             parser, ATT_USER_ALLOWED,
255                                             Delegate.DEFAULT_USER_ALLOWED);
256                                     Delegate d = null;
257                                     if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(
258                                             delegateName)) {
259                                         d = new Delegate(
260                                                 delegateName, delegateId, delegateEnabled,
261                                                 userAllowed);
262                                     }
263                                     r.delegate = d;
264                                 }
265 
266                             }
267 
268                             try {
269                                 deleteDefaultChannelIfNeededLocked(r);
270                             } catch (PackageManager.NameNotFoundException e) {
271                                 Slog.e(TAG, "deleteDefaultChannelIfNeededLocked - Exception: " + e);
272                             }
273                         }
274                     }
275                 }
276             }
277         }
278         throw new IllegalStateException("Failed to reach END_DOCUMENT");
279     }
280 
getPackagePreferencesLocked(String pkg, int uid)281     private PackagePreferences getPackagePreferencesLocked(String pkg, int uid) {
282         final String key = packagePreferencesKey(pkg, uid);
283         return mPackagePreferences.get(key);
284     }
285 
getOrCreatePackagePreferencesLocked(String pkg, int uid)286     private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, int uid) {
287         return getOrCreatePackagePreferencesLocked(pkg, uid,
288                 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
289                 DEFAULT_ALLOW_BUBBLE);
290     }
291 
getOrCreatePackagePreferencesLocked(String pkg, int uid, int importance, int priority, int visibility, boolean showBadge, boolean allowBubble)292     private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, int uid,
293             int importance, int priority, int visibility, boolean showBadge, boolean allowBubble) {
294         final String key = packagePreferencesKey(pkg, uid);
295         PackagePreferences
296                 r = (uid == UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
297                 : mPackagePreferences.get(key);
298         if (r == null) {
299             r = new PackagePreferences();
300             r.pkg = pkg;
301             r.uid = uid;
302             r.importance = importance;
303             r.priority = priority;
304             r.visibility = visibility;
305             r.showBadge = showBadge;
306             r.allowBubble = allowBubble;
307 
308             try {
309                 createDefaultChannelIfNeededLocked(r);
310             } catch (PackageManager.NameNotFoundException e) {
311                 Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
312             }
313 
314             if (r.uid == UNKNOWN_UID) {
315                 mRestoredWithoutUids.put(pkg, r);
316             } else {
317                 mPackagePreferences.put(key, r);
318             }
319         }
320         return r;
321     }
322 
shouldHaveDefaultChannel(PackagePreferences r)323     private boolean shouldHaveDefaultChannel(PackagePreferences r) throws
324             PackageManager.NameNotFoundException {
325         final int userId = UserHandle.getUserId(r.uid);
326         final ApplicationInfo applicationInfo =
327                 mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
328         if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
329             // O apps should not have the default channel.
330             return false;
331         }
332 
333         // Otherwise, this app should have the default channel.
334         return true;
335     }
336 
deleteDefaultChannelIfNeededLocked(PackagePreferences r)337     private boolean deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
338             PackageManager.NameNotFoundException {
339         if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
340             // Not present
341             return false;
342         }
343 
344         if (shouldHaveDefaultChannel(r)) {
345             // Keep the default channel until upgraded.
346             return false;
347         }
348 
349         // Remove Default Channel.
350         r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
351 
352         return true;
353     }
354 
createDefaultChannelIfNeededLocked(PackagePreferences r)355     private boolean createDefaultChannelIfNeededLocked(PackagePreferences r) throws
356             PackageManager.NameNotFoundException {
357         if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
358             r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
359                     com.android.internal.R.string.default_notification_channel_label));
360             return false;
361         }
362 
363         if (!shouldHaveDefaultChannel(r)) {
364             // Keep the default channel until upgraded.
365             return false;
366         }
367 
368         // Create Default Channel
369         NotificationChannel channel;
370         channel = new NotificationChannel(
371                 NotificationChannel.DEFAULT_CHANNEL_ID,
372                 mContext.getString(R.string.default_notification_channel_label),
373                 r.importance);
374         channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
375         channel.setLockscreenVisibility(r.visibility);
376         if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
377             channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
378         }
379         if (r.priority != DEFAULT_PRIORITY) {
380             channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
381         }
382         if (r.visibility != DEFAULT_VISIBILITY) {
383             channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
384         }
385         r.channels.put(channel.getId(), channel);
386 
387         return true;
388     }
389 
writeXml(XmlSerializer out, boolean forBackup, int userId)390     public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
391         out.startTag(null, TAG_RANKING);
392         out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
393         if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS
394                 && (!forBackup || userId == UserHandle.USER_SYSTEM)) {
395             out.startTag(null, TAG_STATUS_ICONS);
396             out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons));
397             out.endTag(null, TAG_STATUS_ICONS);
398         }
399 
400         synchronized (mPackagePreferences) {
401             final int N = mPackagePreferences.size();
402             for (int i = 0; i < N; i++) {
403                 final PackagePreferences r = mPackagePreferences.valueAt(i);
404                 if (forBackup && UserHandle.getUserId(r.uid) != userId) {
405                     continue;
406                 }
407                 final boolean hasNonDefaultSettings =
408                         r.importance != DEFAULT_IMPORTANCE
409                                 || r.priority != DEFAULT_PRIORITY
410                                 || r.visibility != DEFAULT_VISIBILITY
411                                 || r.showBadge != DEFAULT_SHOW_BADGE
412                                 || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
413                                 || r.channels.size() > 0
414                                 || r.groups.size() > 0
415                                 || r.delegate != null
416                                 || r.allowBubble != DEFAULT_ALLOW_BUBBLE;
417                 if (hasNonDefaultSettings) {
418                     out.startTag(null, TAG_PACKAGE);
419                     out.attribute(null, ATT_NAME, r.pkg);
420                     if (r.importance != DEFAULT_IMPORTANCE) {
421                         out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
422                     }
423                     if (r.priority != DEFAULT_PRIORITY) {
424                         out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
425                     }
426                     if (r.visibility != DEFAULT_VISIBILITY) {
427                         out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
428                     }
429                     if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) {
430                         out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble));
431                     }
432                     out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
433                     out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
434                             Integer.toString(r.lockedAppFields));
435 
436                     if (!forBackup) {
437                         out.attribute(null, ATT_UID, Integer.toString(r.uid));
438                     }
439 
440                     if (r.delegate != null) {
441                         out.startTag(null, TAG_DELEGATE);
442 
443                         out.attribute(null, ATT_NAME, r.delegate.mPkg);
444                         out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
445                         if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
446                             out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
447                         }
448                         if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
449                             out.attribute(null, ATT_USER_ALLOWED,
450                                     Boolean.toString(r.delegate.mUserAllowed));
451                         }
452                         out.endTag(null, TAG_DELEGATE);
453                     }
454 
455                     for (NotificationChannelGroup group : r.groups.values()) {
456                         group.writeXml(out);
457                     }
458 
459                     for (NotificationChannel channel : r.channels.values()) {
460                         if (forBackup) {
461                             if (!channel.isDeleted()) {
462                                 channel.writeXmlForBackup(out, mContext);
463                             }
464                         } else {
465                             channel.writeXml(out);
466                         }
467                     }
468 
469                     out.endTag(null, TAG_PACKAGE);
470                 }
471             }
472         }
473         out.endTag(null, TAG_RANKING);
474     }
475 
476     /**
477      * Sets whether bubbles are allowed.
478      *
479      * @param pkg the package to allow or not allow bubbles for.
480      * @param uid the uid to allow or not allow bubbles for.
481      * @param allowed whether bubbles are allowed.
482      */
setBubblesAllowed(String pkg, int uid, boolean allowed)483     public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
484         boolean changed = false;
485         synchronized (mPackagePreferences) {
486             PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid);
487             changed = p.allowBubble != allowed;
488             p.allowBubble = allowed;
489             p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
490         }
491         if (changed) {
492             updateConfig();
493         }
494     }
495 
496     /**
497      * Whether bubbles are allowed.
498      *
499      * @param pkg the package to check if bubbles are allowed for
500      * @param uid the uid to check if bubbles are allowed for.
501      * @return whether bubbles are allowed.
502      */
503     @Override
areBubblesAllowed(String pkg, int uid)504     public boolean areBubblesAllowed(String pkg, int uid) {
505         synchronized (mPackagePreferences) {
506             return getOrCreatePackagePreferencesLocked(pkg, uid).allowBubble;
507         }
508     }
509 
getAppLockedFields(String pkg, int uid)510     public int getAppLockedFields(String pkg, int uid) {
511         synchronized (mPackagePreferences) {
512             return getOrCreatePackagePreferencesLocked(pkg, uid).lockedAppFields;
513         }
514     }
515 
516     /**
517      * Gets importance.
518      */
519     @Override
getImportance(String packageName, int uid)520     public int getImportance(String packageName, int uid) {
521         synchronized (mPackagePreferences) {
522             return getOrCreatePackagePreferencesLocked(packageName, uid).importance;
523         }
524     }
525 
526     /**
527      * Returns whether the importance of the corresponding notification is user-locked and shouldn't
528      * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
529      * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
530      */
getIsAppImportanceLocked(String packageName, int uid)531     public boolean getIsAppImportanceLocked(String packageName, int uid) {
532         synchronized (mPackagePreferences) {
533             int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields;
534             return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
535         }
536     }
537 
538     @Override
canShowBadge(String packageName, int uid)539     public boolean canShowBadge(String packageName, int uid) {
540         synchronized (mPackagePreferences) {
541             return getOrCreatePackagePreferencesLocked(packageName, uid).showBadge;
542         }
543     }
544 
545     @Override
setShowBadge(String packageName, int uid, boolean showBadge)546     public void setShowBadge(String packageName, int uid, boolean showBadge) {
547         synchronized (mPackagePreferences) {
548             getOrCreatePackagePreferencesLocked(packageName, uid).showBadge = showBadge;
549         }
550         updateConfig();
551     }
552 
553     @Override
isGroupBlocked(String packageName, int uid, String groupId)554     public boolean isGroupBlocked(String packageName, int uid, String groupId) {
555         if (groupId == null) {
556             return false;
557         }
558         synchronized (mPackagePreferences) {
559             PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
560             NotificationChannelGroup group = r.groups.get(groupId);
561             if (group == null) {
562                 return false;
563             }
564             return group.isBlocked();
565         }
566     }
567 
getPackagePriority(String pkg, int uid)568     int getPackagePriority(String pkg, int uid) {
569         synchronized (mPackagePreferences) {
570             return getOrCreatePackagePreferencesLocked(pkg, uid).priority;
571         }
572     }
573 
getPackageVisibility(String pkg, int uid)574     int getPackageVisibility(String pkg, int uid) {
575         synchronized (mPackagePreferences) {
576             return getOrCreatePackagePreferencesLocked(pkg, uid).visibility;
577         }
578     }
579 
580     @Override
createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group, boolean fromTargetApp)581     public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
582             boolean fromTargetApp) {
583         Preconditions.checkNotNull(pkg);
584         Preconditions.checkNotNull(group);
585         Preconditions.checkNotNull(group.getId());
586         Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
587         synchronized (mPackagePreferences) {
588             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
589             if (r == null) {
590                 throw new IllegalArgumentException("Invalid package");
591             }
592             final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
593             if (!group.equals(oldGroup)) {
594                 // will log for new entries as well as name/description changes
595                 MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
596             }
597             if (oldGroup != null) {
598                 group.setChannels(oldGroup.getChannels());
599 
600                 // apps can't update the blocked status or app overlay permission
601                 if (fromTargetApp) {
602                     group.setBlocked(oldGroup.isBlocked());
603                     group.unlockFields(group.getUserLockedFields());
604                     group.lockFields(oldGroup.getUserLockedFields());
605                 } else {
606                     // but the system can
607                     if (group.isBlocked() != oldGroup.isBlocked()) {
608                         group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
609                         updateChannelsBypassingDnd(mContext.getUserId());
610                     }
611                 }
612             }
613             r.groups.put(group.getId(), group);
614         }
615     }
616 
617     @Override
createNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromTargetApp, boolean hasDndAccess)618     public boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
619             boolean fromTargetApp, boolean hasDndAccess) {
620         Preconditions.checkNotNull(pkg);
621         Preconditions.checkNotNull(channel);
622         Preconditions.checkNotNull(channel.getId());
623         Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
624         boolean needsPolicyFileChange = false;
625         synchronized (mPackagePreferences) {
626             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
627             if (r == null) {
628                 throw new IllegalArgumentException("Invalid package");
629             }
630             if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
631                 throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
632             }
633             if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
634                 throw new IllegalArgumentException("Reserved id");
635             }
636             NotificationChannel existing = r.channels.get(channel.getId());
637             // Keep most of the existing settings
638             if (existing != null && fromTargetApp) {
639                 if (existing.isDeleted()) {
640                     existing.setDeleted(false);
641                     needsPolicyFileChange = true;
642 
643                     // log a resurrected channel as if it's new again
644                     MetricsLogger.action(getChannelLog(channel, pkg).setType(
645                             com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
646                 }
647 
648                 if (!Objects.equals(channel.getName().toString(), existing.getName().toString())) {
649                     existing.setName(channel.getName().toString());
650                     needsPolicyFileChange = true;
651                 }
652                 if (!Objects.equals(channel.getDescription(), existing.getDescription())) {
653                     existing.setDescription(channel.getDescription());
654                     needsPolicyFileChange = true;
655                 }
656                 if (channel.isBlockableSystem() != existing.isBlockableSystem()) {
657                     existing.setBlockableSystem(channel.isBlockableSystem());
658                     needsPolicyFileChange = true;
659                 }
660                 if (channel.getGroup() != null && existing.getGroup() == null) {
661                     existing.setGroup(channel.getGroup());
662                     needsPolicyFileChange = true;
663                 }
664 
665                 // Apps are allowed to downgrade channel importance if the user has not changed any
666                 // fields on this channel yet.
667                 final int previousExistingImportance = existing.getImportance();
668                 if (existing.getUserLockedFields() == 0 &&
669                         channel.getImportance() < existing.getImportance()) {
670                     existing.setImportance(channel.getImportance());
671                     needsPolicyFileChange = true;
672                 }
673 
674                 // system apps and dnd access apps can bypass dnd if the user hasn't changed any
675                 // fields on the channel yet
676                 if (existing.getUserLockedFields() == 0 && hasDndAccess) {
677                     boolean bypassDnd = channel.canBypassDnd();
678                     if (bypassDnd != existing.canBypassDnd()) {
679                         existing.setBypassDnd(bypassDnd);
680                         needsPolicyFileChange = true;
681 
682                         if (bypassDnd != mAreChannelsBypassingDnd
683                                 || previousExistingImportance != existing.getImportance()) {
684                             updateChannelsBypassingDnd(mContext.getUserId());
685                         }
686                     }
687                 }
688 
689                 updateConfig();
690                 return needsPolicyFileChange;
691             }
692 
693             needsPolicyFileChange = true;
694 
695             if (channel.getImportance() < IMPORTANCE_NONE
696                     || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
697                 throw new IllegalArgumentException("Invalid importance level");
698             }
699 
700             // Reset fields that apps aren't allowed to set.
701             if (fromTargetApp && !hasDndAccess) {
702                 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
703             }
704             if (fromTargetApp) {
705                 channel.setLockscreenVisibility(r.visibility);
706             }
707             clearLockedFieldsLocked(channel);
708             channel.setImportanceLockedByOEM(r.oemLockedImportance);
709             if (!channel.isImportanceLockedByOEM()) {
710                 if (r.futureOemLockedChannels.remove(channel.getId())) {
711                     channel.setImportanceLockedByOEM(true);
712                 }
713             }
714             channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
715             if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
716                 channel.setLockscreenVisibility(
717                         NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
718             }
719             if (!r.showBadge) {
720                 channel.setShowBadge(false);
721             }
722 
723             r.channels.put(channel.getId(), channel);
724             if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
725                 updateChannelsBypassingDnd(mContext.getUserId());
726             }
727             MetricsLogger.action(getChannelLog(channel, pkg).setType(
728                     com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
729         }
730 
731         return needsPolicyFileChange;
732     }
733 
clearLockedFieldsLocked(NotificationChannel channel)734     void clearLockedFieldsLocked(NotificationChannel channel) {
735         channel.unlockFields(channel.getUserLockedFields());
736     }
737 
738     @Override
updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel, boolean fromUser)739     public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
740             boolean fromUser) {
741         Preconditions.checkNotNull(updatedChannel);
742         Preconditions.checkNotNull(updatedChannel.getId());
743         synchronized (mPackagePreferences) {
744             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
745             if (r == null) {
746                 throw new IllegalArgumentException("Invalid package");
747             }
748             NotificationChannel channel = r.channels.get(updatedChannel.getId());
749             if (channel == null || channel.isDeleted()) {
750                 throw new IllegalArgumentException("Channel does not exist");
751             }
752             if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
753                 updatedChannel.setLockscreenVisibility(
754                         NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
755             }
756             if (fromUser) {
757                 updatedChannel.lockFields(channel.getUserLockedFields());
758                 lockFieldsForUpdateLocked(channel, updatedChannel);
759             } else {
760                 updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
761             }
762             // no importance updates are allowed if OEM blocked it
763             updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
764             if (updatedChannel.isImportanceLockedByOEM()) {
765                 updatedChannel.setImportance(channel.getImportance());
766             }
767             updatedChannel.setImportanceLockedByCriticalDeviceFunction(
768                     r.defaultAppLockedImportance);
769             if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
770                     && updatedChannel.getImportance() == IMPORTANCE_NONE) {
771                 updatedChannel.setImportance(channel.getImportance());
772             }
773 
774             r.channels.put(updatedChannel.getId(), updatedChannel);
775 
776             if (onlyHasDefaultChannel(pkg, uid)) {
777                 // copy settings to app level so they are inherited by new channels
778                 // when the app migrates
779                 r.importance = updatedChannel.getImportance();
780                 r.priority = updatedChannel.canBypassDnd()
781                         ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
782                 r.visibility = updatedChannel.getLockscreenVisibility();
783                 r.showBadge = updatedChannel.canShowBadge();
784             }
785 
786             if (!channel.equals(updatedChannel)) {
787                 // only log if there are real changes
788                 MetricsLogger.action(getChannelLog(updatedChannel, pkg)
789                         .setSubtype(fromUser ? 1 : 0));
790             }
791 
792             if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
793                     || channel.getImportance() != updatedChannel.getImportance()) {
794                 updateChannelsBypassingDnd(mContext.getUserId());
795             }
796         }
797         updateConfig();
798     }
799 
800     @Override
getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted)801     public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
802             boolean includeDeleted) {
803         Preconditions.checkNotNull(pkg);
804         synchronized (mPackagePreferences) {
805             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
806             if (r == null) {
807                 return null;
808             }
809             if (channelId == null) {
810                 channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
811             }
812             final NotificationChannel nc = r.channels.get(channelId);
813             if (nc != null && (includeDeleted || !nc.isDeleted())) {
814                 return nc;
815             }
816             return null;
817         }
818     }
819 
820     @Override
deleteNotificationChannel(String pkg, int uid, String channelId)821     public void deleteNotificationChannel(String pkg, int uid, String channelId) {
822         synchronized (mPackagePreferences) {
823             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
824             if (r == null) {
825                 return;
826             }
827             NotificationChannel channel = r.channels.get(channelId);
828             if (channel != null) {
829                 channel.setDeleted(true);
830                 LogMaker lm = getChannelLog(channel, pkg);
831                 lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
832                 MetricsLogger.action(lm);
833 
834                 if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
835                     updateChannelsBypassingDnd(mContext.getUserId());
836                 }
837             }
838         }
839     }
840 
841     @Override
842     @VisibleForTesting
permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId)843     public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
844         Preconditions.checkNotNull(pkg);
845         Preconditions.checkNotNull(channelId);
846         synchronized (mPackagePreferences) {
847             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
848             if (r == null) {
849                 return;
850             }
851             r.channels.remove(channelId);
852         }
853     }
854 
855     @Override
permanentlyDeleteNotificationChannels(String pkg, int uid)856     public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
857         Preconditions.checkNotNull(pkg);
858         synchronized (mPackagePreferences) {
859             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
860             if (r == null) {
861                 return;
862             }
863             int N = r.channels.size() - 1;
864             for (int i = N; i >= 0; i--) {
865                 String key = r.channels.keyAt(i);
866                 if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
867                     r.channels.remove(key);
868                 }
869             }
870         }
871     }
872 
shouldHideSilentStatusIcons()873     public boolean shouldHideSilentStatusIcons() {
874         return mHideSilentStatusBarIcons;
875     }
876 
setHideSilentStatusIcons(boolean hide)877     public void setHideSilentStatusIcons(boolean hide) {
878         mHideSilentStatusBarIcons = hide;
879     }
880 
lockChannelsForOEM(String[] appOrChannelList)881     public void lockChannelsForOEM(String[] appOrChannelList) {
882         if (appOrChannelList == null) {
883             return;
884         }
885         for (String appOrChannel : appOrChannelList) {
886             if (!TextUtils.isEmpty(appOrChannel)) {
887                 String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
888                 if (appSplit != null && appSplit.length > 0) {
889                     String appName = appSplit[0];
890                     String channelId = appSplit.length == 2 ? appSplit[1] : null;
891 
892                     synchronized (mPackagePreferences) {
893                         for (PackagePreferences r : mPackagePreferences.values()) {
894                             if (r.pkg.equals(appName)) {
895                                 if (channelId == null) {
896                                     // lock all channels for the app
897                                     r.oemLockedImportance = true;
898                                     for (NotificationChannel channel : r.channels.values()) {
899                                         channel.setImportanceLockedByOEM(true);
900                                     }
901                                 } else {
902                                     NotificationChannel channel = r.channels.get(channelId);
903                                     if (channel != null) {
904                                         channel.setImportanceLockedByOEM(true);
905                                     } else {
906                                         // if this channel shows up in the future, make sure it'll
907                                         // be locked immediately
908                                         r.futureOemLockedChannels.add(channelId);
909                                     }
910                                 }
911                             }
912                         }
913                     }
914                 }
915             }
916         }
917     }
918 
updateDefaultApps(int userId, ArraySet<String> toRemove, ArraySet<Pair<String, Integer>> toAdd)919     public void updateDefaultApps(int userId, ArraySet<String> toRemove,
920             ArraySet<Pair<String, Integer>> toAdd) {
921         synchronized (mPackagePreferences) {
922             for (PackagePreferences p : mPackagePreferences.values()) {
923                 if (userId == UserHandle.getUserId(p.uid)) {
924                     if (toRemove != null && toRemove.contains(p.pkg)) {
925                         p.defaultAppLockedImportance = false;
926                         for (NotificationChannel channel : p.channels.values()) {
927                             channel.setImportanceLockedByCriticalDeviceFunction(false);
928                         }
929                     }
930                 }
931             }
932             if (toAdd != null) {
933                 for (Pair<String, Integer> approvedApp : toAdd) {
934                     PackagePreferences p = getOrCreatePackagePreferencesLocked(approvedApp.first,
935                             approvedApp.second);
936                     p.defaultAppLockedImportance = true;
937                     for (NotificationChannel channel : p.channels.values()) {
938                         channel.setImportanceLockedByCriticalDeviceFunction(true);
939                     }
940                 }
941             }
942         }
943     }
944 
getNotificationChannelGroupWithChannels(String pkg, int uid, String groupId, boolean includeDeleted)945     public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
946             int uid, String groupId, boolean includeDeleted) {
947         Preconditions.checkNotNull(pkg);
948         synchronized (mPackagePreferences) {
949             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
950             if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
951                 return null;
952             }
953             NotificationChannelGroup group = r.groups.get(groupId).clone();
954             group.setChannels(new ArrayList<>());
955             int N = r.channels.size();
956             for (int i = 0; i < N; i++) {
957                 final NotificationChannel nc = r.channels.valueAt(i);
958                 if (includeDeleted || !nc.isDeleted()) {
959                     if (groupId.equals(nc.getGroup())) {
960                         group.addChannel(nc);
961                     }
962                 }
963             }
964             return group;
965         }
966     }
967 
getNotificationChannelGroup(String groupId, String pkg, int uid)968     public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
969             int uid) {
970         Preconditions.checkNotNull(pkg);
971         synchronized (mPackagePreferences) {
972             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
973             if (r == null) {
974                 return null;
975             }
976             return r.groups.get(groupId);
977         }
978     }
979 
980     @Override
getNotificationChannelGroups(String pkg, int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty)981     public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
982             int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
983         Preconditions.checkNotNull(pkg);
984         Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
985         synchronized (mPackagePreferences) {
986             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
987             if (r == null) {
988                 return ParceledListSlice.emptyList();
989             }
990             NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
991             int N = r.channels.size();
992             for (int i = 0; i < N; i++) {
993                 final NotificationChannel nc = r.channels.valueAt(i);
994                 if (includeDeleted || !nc.isDeleted()) {
995                     if (nc.getGroup() != null) {
996                         if (r.groups.get(nc.getGroup()) != null) {
997                             NotificationChannelGroup ncg = groups.get(nc.getGroup());
998                             if (ncg == null) {
999                                 ncg = r.groups.get(nc.getGroup()).clone();
1000                                 ncg.setChannels(new ArrayList<>());
1001                                 groups.put(nc.getGroup(), ncg);
1002 
1003                             }
1004                             ncg.addChannel(nc);
1005                         }
1006                     } else {
1007                         nonGrouped.addChannel(nc);
1008                     }
1009                 }
1010             }
1011             if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
1012                 groups.put(null, nonGrouped);
1013             }
1014             if (includeEmpty) {
1015                 for (NotificationChannelGroup group : r.groups.values()) {
1016                     if (!groups.containsKey(group.getId())) {
1017                         groups.put(group.getId(), group);
1018                     }
1019                 }
1020             }
1021             return new ParceledListSlice<>(new ArrayList<>(groups.values()));
1022         }
1023     }
1024 
deleteNotificationChannelGroup(String pkg, int uid, String groupId)1025     public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
1026             String groupId) {
1027         List<NotificationChannel> deletedChannels = new ArrayList<>();
1028         synchronized (mPackagePreferences) {
1029             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1030             if (r == null || TextUtils.isEmpty(groupId)) {
1031                 return deletedChannels;
1032             }
1033 
1034             r.groups.remove(groupId);
1035 
1036             int N = r.channels.size();
1037             for (int i = 0; i < N; i++) {
1038                 final NotificationChannel nc = r.channels.valueAt(i);
1039                 if (groupId.equals(nc.getGroup())) {
1040                     nc.setDeleted(true);
1041                     deletedChannels.add(nc);
1042                 }
1043             }
1044         }
1045         return deletedChannels;
1046     }
1047 
1048     @Override
getNotificationChannelGroups(String pkg, int uid)1049     public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
1050             int uid) {
1051         List<NotificationChannelGroup> groups = new ArrayList<>();
1052         synchronized (mPackagePreferences) {
1053             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1054             if (r == null) {
1055                 return groups;
1056             }
1057             groups.addAll(r.groups.values());
1058         }
1059         return groups;
1060     }
1061 
1062     @Override
getNotificationChannels(String pkg, int uid, boolean includeDeleted)1063     public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
1064             boolean includeDeleted) {
1065         Preconditions.checkNotNull(pkg);
1066         List<NotificationChannel> channels = new ArrayList<>();
1067         synchronized (mPackagePreferences) {
1068             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1069             if (r == null) {
1070                 return ParceledListSlice.emptyList();
1071             }
1072             int N = r.channels.size();
1073             for (int i = 0; i < N; i++) {
1074                 final NotificationChannel nc = r.channels.valueAt(i);
1075                 if (includeDeleted || !nc.isDeleted()) {
1076                     channels.add(nc);
1077                 }
1078             }
1079             return new ParceledListSlice<>(channels);
1080         }
1081     }
1082 
1083     /**
1084      * Gets all notification channels associated with the given pkg and userId that can bypass dnd
1085      */
getNotificationChannelsBypassingDnd(String pkg, int userId)1086     public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
1087             int userId) {
1088         List<NotificationChannel> channels = new ArrayList<>();
1089         synchronized (mPackagePreferences) {
1090             final PackagePreferences r = mPackagePreferences.get(
1091                     packagePreferencesKey(pkg, userId));
1092             // notifications from this package aren't blocked
1093             if (r != null && r.importance != IMPORTANCE_NONE) {
1094                 for (NotificationChannel channel : r.channels.values()) {
1095                     if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
1096                         channels.add(channel);
1097                     }
1098                 }
1099             }
1100         }
1101         return new ParceledListSlice<>(channels);
1102     }
1103 
1104     /**
1105      * True for pre-O apps that only have the default channel, or pre O apps that have no
1106      * channels yet. This method will create the default channel for pre-O apps that don't have it.
1107      * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
1108      * upgrades.
1109      */
onlyHasDefaultChannel(String pkg, int uid)1110     public boolean onlyHasDefaultChannel(String pkg, int uid) {
1111         synchronized (mPackagePreferences) {
1112             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1113             if (r.channels.size() == 1
1114                     && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
1115                 return true;
1116             }
1117             return false;
1118         }
1119     }
1120 
getDeletedChannelCount(String pkg, int uid)1121     public int getDeletedChannelCount(String pkg, int uid) {
1122         Preconditions.checkNotNull(pkg);
1123         int deletedCount = 0;
1124         synchronized (mPackagePreferences) {
1125             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1126             if (r == null) {
1127                 return deletedCount;
1128             }
1129             int N = r.channels.size();
1130             for (int i = 0; i < N; i++) {
1131                 final NotificationChannel nc = r.channels.valueAt(i);
1132                 if (nc.isDeleted()) {
1133                     deletedCount++;
1134                 }
1135             }
1136             return deletedCount;
1137         }
1138     }
1139 
getBlockedChannelCount(String pkg, int uid)1140     public int getBlockedChannelCount(String pkg, int uid) {
1141         Preconditions.checkNotNull(pkg);
1142         int blockedCount = 0;
1143         synchronized (mPackagePreferences) {
1144             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1145             if (r == null) {
1146                 return blockedCount;
1147             }
1148             int N = r.channels.size();
1149             for (int i = 0; i < N; i++) {
1150                 final NotificationChannel nc = r.channels.valueAt(i);
1151                 if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
1152                     blockedCount++;
1153                 }
1154             }
1155             return blockedCount;
1156         }
1157     }
1158 
getBlockedAppCount(int userId)1159     public int getBlockedAppCount(int userId) {
1160         int count = 0;
1161         synchronized (mPackagePreferences) {
1162             final int N = mPackagePreferences.size();
1163             for (int i = 0; i < N; i++) {
1164                 final PackagePreferences r = mPackagePreferences.valueAt(i);
1165                 if (userId == UserHandle.getUserId(r.uid)
1166                         && r.importance == IMPORTANCE_NONE) {
1167                     count++;
1168                 }
1169             }
1170         }
1171         return count;
1172     }
1173 
1174     /**
1175      * Returns the number of apps that have at least one notification channel that can bypass DND
1176      * for given particular user
1177      */
getAppsBypassingDndCount(int userId)1178     public int getAppsBypassingDndCount(int userId) {
1179         int count = 0;
1180         synchronized (mPackagePreferences) {
1181             final int numPackagePreferences = mPackagePreferences.size();
1182             for (int i = 0; i < numPackagePreferences; i++) {
1183                 final PackagePreferences r = mPackagePreferences.valueAt(i);
1184                 // Package isn't associated with this userId or notifications from this package are
1185                 // blocked
1186                 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1187                     continue;
1188                 }
1189 
1190                 for (NotificationChannel channel : r.channels.values()) {
1191                     if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
1192                         count++;
1193                         break;
1194                     }
1195                 }
1196             }
1197         }
1198         return count;
1199     }
1200 
1201     /**
1202      * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
1203      * updating
1204      * @param userId
1205      */
syncChannelsBypassingDnd(int userId)1206     private void syncChannelsBypassingDnd(int userId) {
1207         mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
1208                 & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
1209         updateChannelsBypassingDnd(userId);
1210     }
1211 
1212     /**
1213      * Updates the user's NotificationPolicy based on whether the given userId
1214      * has channels bypassing DND
1215      * @param userId
1216      */
updateChannelsBypassingDnd(int userId)1217     private void updateChannelsBypassingDnd(int userId) {
1218         synchronized (mPackagePreferences) {
1219             final int numPackagePreferences = mPackagePreferences.size();
1220             for (int i = 0; i < numPackagePreferences; i++) {
1221                 final PackagePreferences r = mPackagePreferences.valueAt(i);
1222                 // Package isn't associated with this userId or notifications from this package are
1223                 // blocked
1224                 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1225                     continue;
1226                 }
1227 
1228                 for (NotificationChannel channel : r.channels.values()) {
1229                     if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
1230                         if (!mAreChannelsBypassingDnd) {
1231                             mAreChannelsBypassingDnd = true;
1232                             updateZenPolicy(true);
1233                         }
1234                         return;
1235                     }
1236                 }
1237             }
1238         }
1239         // If no channels bypass DND, update the zen policy once to disable DND bypass.
1240         if (mAreChannelsBypassingDnd) {
1241             mAreChannelsBypassingDnd = false;
1242             updateZenPolicy(false);
1243         }
1244     }
1245 
channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel)1246     private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
1247         // Channel is in a group that's blocked
1248         if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
1249             return false;
1250         }
1251 
1252         // Channel is deleted or is blocked
1253         if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
1254             return false;
1255         }
1256 
1257         return true;
1258     }
1259 
updateZenPolicy(boolean areChannelsBypassingDnd)1260     public void updateZenPolicy(boolean areChannelsBypassingDnd) {
1261         NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
1262         mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
1263                 policy.priorityCategories, policy.priorityCallSenders,
1264                 policy.priorityMessageSenders, policy.suppressedVisualEffects,
1265                 (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
1266                         : 0)));
1267     }
1268 
areChannelsBypassingDnd()1269     public boolean areChannelsBypassingDnd() {
1270         return mAreChannelsBypassingDnd;
1271     }
1272 
1273     /**
1274      * Sets importance.
1275      */
1276     @Override
setImportance(String pkgName, int uid, int importance)1277     public void setImportance(String pkgName, int uid, int importance) {
1278         synchronized (mPackagePreferences) {
1279             getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance;
1280         }
1281         updateConfig();
1282     }
1283 
setEnabled(String packageName, int uid, boolean enabled)1284     public void setEnabled(String packageName, int uid, boolean enabled) {
1285         boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
1286         if (wasEnabled == enabled) {
1287             return;
1288         }
1289         setImportance(packageName, uid,
1290                 enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
1291     }
1292 
1293     /**
1294      * Sets whether any notifications from the app, represented by the given {@code pkgName} and
1295      * {@code uid}, have their importance locked by the user. Locked notifications don't get
1296      * considered for sentiment adjustments (and thus never show a blocking helper).
1297      */
setAppImportanceLocked(String packageName, int uid)1298     public void setAppImportanceLocked(String packageName, int uid) {
1299         synchronized (mPackagePreferences) {
1300             PackagePreferences prefs = getOrCreatePackagePreferencesLocked(packageName, uid);
1301             if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
1302                 return;
1303             }
1304 
1305             prefs.lockedAppFields =
1306                     prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
1307         }
1308         updateConfig();
1309     }
1310 
1311     /**
1312      * Returns the delegate for a given package, if it's allowed by the package and the user.
1313      */
getNotificationDelegate(String sourcePkg, int sourceUid)1314     public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
1315         synchronized (mPackagePreferences) {
1316             PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1317 
1318             if (prefs == null || prefs.delegate == null) {
1319                 return null;
1320             }
1321             if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
1322                 return null;
1323             }
1324             return prefs.delegate.mPkg;
1325         }
1326     }
1327 
1328     /**
1329      * Used by an app to delegate notification posting privileges to another apps.
1330      */
setNotificationDelegate(String sourcePkg, int sourceUid, String delegatePkg, int delegateUid)1331     public void setNotificationDelegate(String sourcePkg, int sourceUid,
1332             String delegatePkg, int delegateUid) {
1333         synchronized (mPackagePreferences) {
1334             PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid);
1335 
1336             boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
1337             Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
1338             prefs.delegate = delegate;
1339         }
1340         updateConfig();
1341     }
1342 
1343     /**
1344      * Used by an app to turn off its notification delegate.
1345      */
revokeNotificationDelegate(String sourcePkg, int sourceUid)1346     public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
1347         boolean changed = false;
1348         synchronized (mPackagePreferences) {
1349             PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1350             if (prefs != null && prefs.delegate != null) {
1351                 prefs.delegate.mEnabled = false;
1352                 changed = true;
1353             }
1354         }
1355         if (changed) {
1356             updateConfig();
1357         }
1358     }
1359 
1360     /**
1361      * Toggles whether an app can have a notification delegate on behalf of a user.
1362      */
toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed)1363     public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
1364         boolean changed = false;
1365         synchronized (mPackagePreferences) {
1366             PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1367             if (prefs != null && prefs.delegate != null) {
1368                 prefs.delegate.mUserAllowed = userAllowed;
1369                 changed = true;
1370             }
1371         }
1372         if (changed) {
1373             updateConfig();
1374         }
1375     }
1376 
1377     /**
1378      * Returns whether the given app is allowed on post notifications on behalf of the other given
1379      * app.
1380      */
isDelegateAllowed(String sourcePkg, int sourceUid, String potentialDelegatePkg, int potentialDelegateUid)1381     public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
1382             String potentialDelegatePkg, int potentialDelegateUid) {
1383         synchronized (mPackagePreferences) {
1384             PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1385 
1386             return prefs != null && prefs.isValidDelegate(potentialDelegatePkg,
1387                     potentialDelegateUid);
1388         }
1389     }
1390 
1391     @VisibleForTesting
lockFieldsForUpdateLocked(NotificationChannel original, NotificationChannel update)1392     void lockFieldsForUpdateLocked(NotificationChannel original, NotificationChannel update) {
1393         if (original.canBypassDnd() != update.canBypassDnd()) {
1394             update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
1395         }
1396         if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
1397             update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
1398         }
1399         if (original.getImportance() != update.getImportance()) {
1400             update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
1401         }
1402         if (original.shouldShowLights() != update.shouldShowLights()
1403                 || original.getLightColor() != update.getLightColor()) {
1404             update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
1405         }
1406         if (!Objects.equals(original.getSound(), update.getSound())) {
1407             update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
1408         }
1409         if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
1410                 || original.shouldVibrate() != update.shouldVibrate()) {
1411             update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
1412         }
1413         if (original.canShowBadge() != update.canShowBadge()) {
1414             update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
1415         }
1416         if (original.canBubble() != update.canBubble()) {
1417             update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE);
1418         }
1419     }
1420 
dump(PrintWriter pw, String prefix, @NonNull NotificationManagerService.DumpFilter filter)1421     public void dump(PrintWriter pw, String prefix,
1422             @NonNull NotificationManagerService.DumpFilter filter) {
1423         pw.print(prefix);
1424         pw.println("per-package config:");
1425 
1426         pw.println("PackagePreferences:");
1427         synchronized (mPackagePreferences) {
1428             dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences);
1429         }
1430         pw.println("Restored without uid:");
1431         dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids);
1432     }
1433 
dump(ProtoOutputStream proto, @NonNull NotificationManagerService.DumpFilter filter)1434     public void dump(ProtoOutputStream proto,
1435             @NonNull NotificationManagerService.DumpFilter filter) {
1436         synchronized (mPackagePreferences) {
1437             dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
1438                     mPackagePreferences);
1439         }
1440         dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
1441                 mRestoredWithoutUids);
1442     }
1443 
dumpPackagePreferencesLocked(PrintWriter pw, String prefix, @NonNull NotificationManagerService.DumpFilter filter, ArrayMap<String, PackagePreferences> packagePreferences)1444     private static void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
1445             @NonNull NotificationManagerService.DumpFilter filter,
1446             ArrayMap<String, PackagePreferences> packagePreferences) {
1447         final int N = packagePreferences.size();
1448         for (int i = 0; i < N; i++) {
1449             final PackagePreferences r = packagePreferences.valueAt(i);
1450             if (filter.matches(r.pkg)) {
1451                 pw.print(prefix);
1452                 pw.print("  AppSettings: ");
1453                 pw.print(r.pkg);
1454                 pw.print(" (");
1455                 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
1456                 pw.print(')');
1457                 if (r.importance != DEFAULT_IMPORTANCE) {
1458                     pw.print(" importance=");
1459                     pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
1460                 }
1461                 if (r.priority != DEFAULT_PRIORITY) {
1462                     pw.print(" priority=");
1463                     pw.print(Notification.priorityToString(r.priority));
1464                 }
1465                 if (r.visibility != DEFAULT_VISIBILITY) {
1466                     pw.print(" visibility=");
1467                     pw.print(Notification.visibilityToString(r.visibility));
1468                 }
1469                 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1470                     pw.print(" showBadge=");
1471                     pw.print(r.showBadge);
1472                 }
1473                 if (r.defaultAppLockedImportance != DEFAULT_APP_LOCKED_IMPORTANCE) {
1474                     pw.print(" defaultAppLocked=");
1475                     pw.print(r.defaultAppLockedImportance);
1476                 }
1477                 if (r.oemLockedImportance != DEFAULT_OEM_LOCKED_IMPORTANCE) {
1478                     pw.print(" oemLocked=");
1479                     pw.print(r.oemLockedImportance);
1480                 }
1481                 if (!r.futureOemLockedChannels.isEmpty()) {
1482                     pw.print(" futureLockedChannels=");
1483                     pw.print(r.futureOemLockedChannels);
1484                 }
1485                 pw.println();
1486                 for (NotificationChannel channel : r.channels.values()) {
1487                     pw.print(prefix);
1488                     channel.dump(pw, "    ", filter.redact);
1489                 }
1490                 for (NotificationChannelGroup group : r.groups.values()) {
1491                     pw.print(prefix);
1492                     pw.print("  ");
1493                     pw.print("  ");
1494                     pw.println(group);
1495                 }
1496             }
1497         }
1498     }
1499 
dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId, @NonNull NotificationManagerService.DumpFilter filter, ArrayMap<String, PackagePreferences> packagePreferences)1500     private static void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
1501             @NonNull NotificationManagerService.DumpFilter filter,
1502             ArrayMap<String, PackagePreferences> packagePreferences) {
1503         final int N = packagePreferences.size();
1504         long fToken;
1505         for (int i = 0; i < N; i++) {
1506             final PackagePreferences r = packagePreferences.valueAt(i);
1507             if (filter.matches(r.pkg)) {
1508                 fToken = proto.start(fieldId);
1509 
1510                 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
1511                 proto.write(RankingHelperProto.RecordProto.UID, r.uid);
1512                 proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
1513                 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
1514                 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
1515                 proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
1516 
1517                 for (NotificationChannel channel : r.channels.values()) {
1518                     channel.writeToProto(proto, RankingHelperProto.RecordProto.CHANNELS);
1519                 }
1520                 for (NotificationChannelGroup group : r.groups.values()) {
1521                     group.writeToProto(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS);
1522                 }
1523 
1524                 proto.end(fToken);
1525             }
1526         }
1527     }
1528 
dumpJson(NotificationManagerService.DumpFilter filter)1529     public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
1530         JSONObject ranking = new JSONObject();
1531         JSONArray PackagePreferencess = new JSONArray();
1532         try {
1533             ranking.put("noUid", mRestoredWithoutUids.size());
1534         } catch (JSONException e) {
1535             // pass
1536         }
1537         synchronized (mPackagePreferences) {
1538             final int N = mPackagePreferences.size();
1539             for (int i = 0; i < N; i++) {
1540                 final PackagePreferences r = mPackagePreferences.valueAt(i);
1541                 if (filter == null || filter.matches(r.pkg)) {
1542                     JSONObject PackagePreferences = new JSONObject();
1543                     try {
1544                         PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
1545                         PackagePreferences.put("packageName", r.pkg);
1546                         if (r.importance != DEFAULT_IMPORTANCE) {
1547                             PackagePreferences.put("importance",
1548                                     NotificationListenerService.Ranking.importanceToString(
1549                                             r.importance));
1550                         }
1551                         if (r.priority != DEFAULT_PRIORITY) {
1552                             PackagePreferences.put("priority",
1553                                     Notification.priorityToString(r.priority));
1554                         }
1555                         if (r.visibility != DEFAULT_VISIBILITY) {
1556                             PackagePreferences.put("visibility",
1557                                     Notification.visibilityToString(r.visibility));
1558                         }
1559                         if (r.showBadge != DEFAULT_SHOW_BADGE) {
1560                             PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
1561                         }
1562                         JSONArray channels = new JSONArray();
1563                         for (NotificationChannel channel : r.channels.values()) {
1564                             channels.put(channel.toJson());
1565                         }
1566                         PackagePreferences.put("channels", channels);
1567                         JSONArray groups = new JSONArray();
1568                         for (NotificationChannelGroup group : r.groups.values()) {
1569                             groups.put(group.toJson());
1570                         }
1571                         PackagePreferences.put("groups", groups);
1572                     } catch (JSONException e) {
1573                         // pass
1574                     }
1575                     PackagePreferencess.put(PackagePreferences);
1576                 }
1577             }
1578         }
1579         try {
1580             ranking.put("PackagePreferencess", PackagePreferencess);
1581         } catch (JSONException e) {
1582             // pass
1583         }
1584         return ranking;
1585     }
1586 
1587     /**
1588      * Dump only the ban information as structured JSON for the stats collector.
1589      *
1590      * This is intentionally redundant with {#link dumpJson} because the old
1591      * scraper will expect this format.
1592      *
1593      * @param filter
1594      * @return
1595      */
dumpBansJson(NotificationManagerService.DumpFilter filter)1596     public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
1597         JSONArray bans = new JSONArray();
1598         Map<Integer, String> packageBans = getPackageBans();
1599         for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
1600             final int userId = UserHandle.getUserId(ban.getKey());
1601             final String packageName = ban.getValue();
1602             if (filter == null || filter.matches(packageName)) {
1603                 JSONObject banJson = new JSONObject();
1604                 try {
1605                     banJson.put("userId", userId);
1606                     banJson.put("packageName", packageName);
1607                 } catch (JSONException e) {
1608                     e.printStackTrace();
1609                 }
1610                 bans.put(banJson);
1611             }
1612         }
1613         return bans;
1614     }
1615 
getPackageBans()1616     public Map<Integer, String> getPackageBans() {
1617         synchronized (mPackagePreferences) {
1618             final int N = mPackagePreferences.size();
1619             ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
1620             for (int i = 0; i < N; i++) {
1621                 final PackagePreferences r = mPackagePreferences.valueAt(i);
1622                 if (r.importance == IMPORTANCE_NONE) {
1623                     packageBans.put(r.uid, r.pkg);
1624                 }
1625             }
1626 
1627             return packageBans;
1628         }
1629     }
1630 
1631     /**
1632      * Dump only the channel information as structured JSON for the stats collector.
1633      *
1634      * This is intentionally redundant with {#link dumpJson} because the old
1635      * scraper will expect this format.
1636      *
1637      * @param filter
1638      * @return
1639      */
dumpChannelsJson(NotificationManagerService.DumpFilter filter)1640     public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
1641         JSONArray channels = new JSONArray();
1642         Map<String, Integer> packageChannels = getPackageChannels();
1643         for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) {
1644             final String packageName = channelCount.getKey();
1645             if (filter == null || filter.matches(packageName)) {
1646                 JSONObject channelCountJson = new JSONObject();
1647                 try {
1648                     channelCountJson.put("packageName", packageName);
1649                     channelCountJson.put("channelCount", channelCount.getValue());
1650                 } catch (JSONException e) {
1651                     e.printStackTrace();
1652                 }
1653                 channels.put(channelCountJson);
1654             }
1655         }
1656         return channels;
1657     }
1658 
getPackageChannels()1659     private Map<String, Integer> getPackageChannels() {
1660         ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
1661         synchronized (mPackagePreferences) {
1662             for (int i = 0; i < mPackagePreferences.size(); i++) {
1663                 final PackagePreferences r = mPackagePreferences.valueAt(i);
1664                 int channelCount = 0;
1665                 for (int j = 0; j < r.channels.size(); j++) {
1666                     if (!r.channels.valueAt(j).isDeleted()) {
1667                         channelCount++;
1668                     }
1669                 }
1670                 packageChannels.put(r.pkg, channelCount);
1671             }
1672         }
1673         return packageChannels;
1674     }
1675 
1676     /**
1677      * Called when user switches
1678      */
onUserSwitched(int userId)1679     public void onUserSwitched(int userId) {
1680         syncChannelsBypassingDnd(userId);
1681     }
1682 
1683     /**
1684      * Called when user is unlocked
1685      */
onUserUnlocked(int userId)1686     public void onUserUnlocked(int userId) {
1687         syncChannelsBypassingDnd(userId);
1688     }
1689 
onUserRemoved(int userId)1690     public void onUserRemoved(int userId) {
1691         synchronized (mPackagePreferences) {
1692             int N = mPackagePreferences.size();
1693             for (int i = N - 1; i >= 0; i--) {
1694                 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
1695                 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
1696                     mPackagePreferences.removeAt(i);
1697                 }
1698             }
1699         }
1700     }
1701 
onLocaleChanged(Context context, int userId)1702     protected void onLocaleChanged(Context context, int userId) {
1703         synchronized (mPackagePreferences) {
1704             int N = mPackagePreferences.size();
1705             for (int i = 0; i < N; i++) {
1706                 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
1707                 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
1708                     if (PackagePreferences.channels.containsKey(
1709                             NotificationChannel.DEFAULT_CHANNEL_ID)) {
1710                         PackagePreferences.channels.get(
1711                                 NotificationChannel.DEFAULT_CHANNEL_ID).setName(
1712                                 context.getResources().getString(
1713                                         R.string.default_notification_channel_label));
1714                     }
1715                 }
1716             }
1717         }
1718     }
1719 
onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList, int[] uidList)1720     public boolean onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
1721             int[] uidList) {
1722         if (pkgList == null || pkgList.length == 0) {
1723             return false; // nothing to do
1724         }
1725         boolean updated = false;
1726         if (removingPackage) {
1727             // Remove notification settings for uninstalled package
1728             int size = Math.min(pkgList.length, uidList.length);
1729             for (int i = 0; i < size; i++) {
1730                 final String pkg = pkgList[i];
1731                 final int uid = uidList[i];
1732                 synchronized (mPackagePreferences) {
1733                     mPackagePreferences.remove(packagePreferencesKey(pkg, uid));
1734                 }
1735                 mRestoredWithoutUids.remove(pkg);
1736                 updated = true;
1737             }
1738         } else {
1739             for (String pkg : pkgList) {
1740                 // Package install
1741                 final PackagePreferences r = mRestoredWithoutUids.get(pkg);
1742                 if (r != null) {
1743                     try {
1744                         r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
1745                         mRestoredWithoutUids.remove(pkg);
1746                         synchronized (mPackagePreferences) {
1747                             mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
1748                         }
1749                         updated = true;
1750                     } catch (PackageManager.NameNotFoundException e) {
1751                         // noop
1752                     }
1753                 }
1754                 // Package upgrade
1755                 try {
1756                     synchronized (mPackagePreferences) {
1757                         PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg,
1758                                 mPm.getPackageUidAsUser(pkg, changeUserId));
1759                         if (fullPackagePreferences != null) {
1760                             updated |= createDefaultChannelIfNeededLocked(fullPackagePreferences);
1761                             updated |= deleteDefaultChannelIfNeededLocked(fullPackagePreferences);
1762                         }
1763                     }
1764                 } catch (PackageManager.NameNotFoundException e) {
1765                 }
1766             }
1767         }
1768 
1769         if (updated) {
1770             updateConfig();
1771         }
1772         return updated;
1773     }
1774 
clearData(String pkg, int uid)1775     public void clearData(String pkg, int uid) {
1776         synchronized (mPackagePreferences) {
1777             PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
1778             if (p != null) {
1779                 p.channels = new ArrayMap<>();
1780                 p.groups = new ArrayMap<>();
1781                 p.delegate = null;
1782                 p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
1783                 p.allowBubble = DEFAULT_ALLOW_BUBBLE;
1784                 p.importance = DEFAULT_IMPORTANCE;
1785                 p.priority = DEFAULT_PRIORITY;
1786                 p.visibility = DEFAULT_VISIBILITY;
1787                 p.showBadge = DEFAULT_SHOW_BADGE;
1788             }
1789         }
1790     }
1791 
getChannelLog(NotificationChannel channel, String pkg)1792     private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
1793         return new LogMaker(
1794                 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1795                         .ACTION_NOTIFICATION_CHANNEL)
1796                 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1797                 .setPackageName(pkg)
1798                 .addTaggedData(
1799                         com.android.internal.logging.nano.MetricsProto.MetricsEvent
1800                                 .FIELD_NOTIFICATION_CHANNEL_ID,
1801                         channel.getId())
1802                 .addTaggedData(
1803                         com.android.internal.logging.nano.MetricsProto.MetricsEvent
1804                                 .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
1805                         channel.getImportance());
1806     }
1807 
getChannelGroupLog(String groupId, String pkg)1808     private LogMaker getChannelGroupLog(String groupId, String pkg) {
1809         return new LogMaker(
1810                 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1811                         .ACTION_NOTIFICATION_CHANNEL_GROUP)
1812                 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1813                 .addTaggedData(
1814                         com.android.internal.logging.nano.MetricsProto.MetricsEvent
1815                                 .FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
1816                         groupId)
1817                 .setPackageName(pkg);
1818     }
1819 
updateBubblesEnabled()1820     public void updateBubblesEnabled() {
1821         if (mBubblesEnabled == null) {
1822             mBubblesEnabled = new SparseBooleanArray();
1823         }
1824         boolean changed = false;
1825         // update the cached values
1826         for (int index = 0; index < mBubblesEnabled.size(); index++) {
1827             int userId = mBubblesEnabled.keyAt(index);
1828             final boolean oldValue = mBubblesEnabled.get(userId);
1829             final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1830                     Settings.Secure.NOTIFICATION_BUBBLES,
1831                     DEFAULT_ALLOW_BUBBLE ? 1 : 0, userId) != 0;
1832             mBubblesEnabled.put(userId, newValue);
1833             changed |= oldValue != newValue;
1834         }
1835         if (changed) {
1836             updateConfig();
1837         }
1838     }
1839 
bubblesEnabled(UserHandle userHandle)1840     public boolean bubblesEnabled(UserHandle userHandle) {
1841         int userId = userHandle.getIdentifier();
1842         if (userId == UserHandle.USER_ALL) {
1843             return false;
1844         }
1845         if (mBubblesEnabled.indexOfKey(userId) < 0) {
1846             mBubblesEnabled.put(userId,
1847                     Settings.Secure.getIntForUser(mContext.getContentResolver(),
1848                             Settings.Secure.NOTIFICATION_BUBBLES,
1849                             DEFAULT_ALLOW_BUBBLE ? 1 : 0, userId) != 0);
1850         }
1851         return mBubblesEnabled.get(userId, DEFAULT_ALLOW_BUBBLE);
1852     }
1853 
1854 
updateBadgingEnabled()1855     public void updateBadgingEnabled() {
1856         if (mBadgingEnabled == null) {
1857             mBadgingEnabled = new SparseBooleanArray();
1858         }
1859         boolean changed = false;
1860         // update the cached values
1861         for (int index = 0; index < mBadgingEnabled.size(); index++) {
1862             int userId = mBadgingEnabled.keyAt(index);
1863             final boolean oldValue = mBadgingEnabled.get(userId);
1864             final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1865                     Settings.Secure.NOTIFICATION_BADGING,
1866                     DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
1867             mBadgingEnabled.put(userId, newValue);
1868             changed |= oldValue != newValue;
1869         }
1870         if (changed) {
1871             updateConfig();
1872         }
1873     }
1874 
badgingEnabled(UserHandle userHandle)1875     public boolean badgingEnabled(UserHandle userHandle) {
1876         int userId = userHandle.getIdentifier();
1877         if (userId == UserHandle.USER_ALL) {
1878             return false;
1879         }
1880         if (mBadgingEnabled.indexOfKey(userId) < 0) {
1881             mBadgingEnabled.put(userId,
1882                     Settings.Secure.getIntForUser(mContext.getContentResolver(),
1883                             Settings.Secure.NOTIFICATION_BADGING,
1884                             DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
1885         }
1886         return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
1887     }
1888 
updateConfig()1889     private void updateConfig() {
1890         mRankingHandler.requestSort();
1891     }
1892 
packagePreferencesKey(String pkg, int uid)1893     private static String packagePreferencesKey(String pkg, int uid) {
1894         return pkg + "|" + uid;
1895     }
1896 
1897     private static class PackagePreferences {
1898         String pkg;
1899         int uid = UNKNOWN_UID;
1900         int importance = DEFAULT_IMPORTANCE;
1901         int priority = DEFAULT_PRIORITY;
1902         int visibility = DEFAULT_VISIBILITY;
1903         boolean showBadge = DEFAULT_SHOW_BADGE;
1904         boolean allowBubble = DEFAULT_ALLOW_BUBBLE;
1905         int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
1906         // these fields are loaded on boot from a different source of truth and so are not
1907         // written to notification policy xml
1908         boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
1909         List<String> futureOemLockedChannels = new ArrayList<>();
1910         boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE;
1911 
1912         Delegate delegate = null;
1913         ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
1914         Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
1915 
isValidDelegate(String pkg, int uid)1916         public boolean isValidDelegate(String pkg, int uid) {
1917             return delegate != null && delegate.isAllowed(pkg, uid);
1918         }
1919     }
1920 
1921     private static class Delegate {
1922         static final boolean DEFAULT_ENABLED = true;
1923         static final boolean DEFAULT_USER_ALLOWED = true;
1924         String mPkg;
1925         int mUid = UNKNOWN_UID;
1926         boolean mEnabled = DEFAULT_ENABLED;
1927         boolean mUserAllowed = DEFAULT_USER_ALLOWED;
1928 
Delegate(String pkg, int uid, boolean enabled, boolean userAllowed)1929         Delegate(String pkg, int uid, boolean enabled, boolean userAllowed) {
1930             mPkg = pkg;
1931             mUid = uid;
1932             mEnabled = enabled;
1933             mUserAllowed = userAllowed;
1934         }
1935 
isAllowed(String pkg, int uid)1936         public boolean isAllowed(String pkg, int uid) {
1937             if (pkg == null || uid == UNKNOWN_UID) {
1938                 return false;
1939             }
1940             return pkg.equals(mPkg)
1941                     && uid == mUid
1942                     && (mUserAllowed && mEnabled);
1943         }
1944     }
1945 }
1946