blob: 7c0e0b0983fb9cdfdbe81fa9b93f6265104cb211 [file] [log] [blame]
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001/**
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
17package com.android.server.notification;
18
19import static android.app.NotificationManager.IMPORTANCE_NONE;
20
21import android.annotation.IntDef;
22import android.annotation.NonNull;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040023import android.annotation.Nullable;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040024import android.app.Notification;
25import android.app.NotificationChannel;
26import android.app.NotificationChannelGroup;
27import android.app.NotificationManager;
28import android.content.Context;
29import android.content.pm.ApplicationInfo;
30import android.content.pm.PackageManager;
31import android.content.pm.ParceledListSlice;
32import android.metrics.LogMaker;
33import android.os.Build;
34import android.os.UserHandle;
35import android.provider.Settings;
36import android.service.notification.NotificationListenerService;
37import android.service.notification.RankingHelperProto;
38import android.text.TextUtils;
39import android.util.ArrayMap;
40import android.util.Slog;
41import android.util.SparseBooleanArray;
42import android.util.proto.ProtoOutputStream;
43
44import com.android.internal.R;
45import com.android.internal.annotations.VisibleForTesting;
46import com.android.internal.logging.MetricsLogger;
47import com.android.internal.util.Preconditions;
48import com.android.internal.util.XmlUtils;
49
50import org.json.JSONArray;
51import org.json.JSONException;
52import org.json.JSONObject;
53import org.xmlpull.v1.XmlPullParser;
54import org.xmlpull.v1.XmlPullParserException;
55import org.xmlpull.v1.XmlSerializer;
56
57import java.io.IOException;
58import java.io.PrintWriter;
59import java.util.ArrayList;
60import java.util.Arrays;
61import java.util.Collection;
62import java.util.List;
63import java.util.Map;
64import java.util.Objects;
65import java.util.concurrent.ConcurrentHashMap;
66
67public class PreferencesHelper implements RankingConfig {
68 private static final String TAG = "NotificationPrefHelper";
69 private static final int XML_VERSION = 1;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040070 private static final int UNKNOWN_UID = UserHandle.USER_NULL;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040071
72 @VisibleForTesting
73 static final String TAG_RANKING = "ranking";
74 private static final String TAG_PACKAGE = "package";
75 private static final String TAG_CHANNEL = "channel";
76 private static final String TAG_GROUP = "channelGroup";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040077 private static final String TAG_DELEGATE = "delegate";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040078
79 private static final String ATT_VERSION = "version";
80 private static final String ATT_NAME = "name";
81 private static final String ATT_UID = "uid";
82 private static final String ATT_ID = "id";
Julia Reynolds33ab8a02018-12-17 16:19:52 -050083 private static final String ATT_APP_OVERLAY = "overlay";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040084 private static final String ATT_PRIORITY = "priority";
85 private static final String ATT_VISIBILITY = "visibility";
86 private static final String ATT_IMPORTANCE = "importance";
87 private static final String ATT_SHOW_BADGE = "show_badge";
88 private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040089 private static final String ATT_ENABLED = "enabled";
90 private static final String ATT_USER_ALLOWED = "allowed";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040091
92 private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
93 private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
94 private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
95 private static final boolean DEFAULT_SHOW_BADGE = true;
Julia Reynolds33ab8a02018-12-17 16:19:52 -050096 private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040097 /**
98 * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
99 * fields.
100 */
101 private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
102
103 /**
104 * All user-lockable fields for a given application.
105 */
106 @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
107 public @interface LockableAppFields {
108 int USER_LOCKED_IMPORTANCE = 0x00000001;
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500109 int USER_LOCKED_APP_OVERLAY = 0x00000002;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400110 }
111
112 // pkg|uid => PackagePreferences
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400113 private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400114 // pkg => PackagePreferences
115 private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
116
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400117 private final Context mContext;
118 private final PackageManager mPm;
119 private final RankingHandler mRankingHandler;
120 private final ZenModeHelper mZenModeHelper;
121
122 private SparseBooleanArray mBadgingEnabled;
123 private boolean mAreChannelsBypassingDnd;
124
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400125 public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
126 ZenModeHelper zenHelper) {
127 mContext = context;
128 mZenModeHelper = zenHelper;
129 mRankingHandler = rankingHandler;
130 mPm = pm;
131
132 updateBadgingEnabled();
Beverly0479cde22018-11-09 11:05:34 -0500133 syncChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400134 }
135
136 public void readXml(XmlPullParser parser, boolean forRestore)
137 throws XmlPullParserException, IOException {
138 int type = parser.getEventType();
139 if (type != XmlPullParser.START_TAG) return;
140 String tag = parser.getName();
141 if (!TAG_RANKING.equals(tag)) return;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400142 synchronized (mPackagePreferences) {
143 // Clobber groups and channels with the xml, but don't delete other data that wasn't present
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400144
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400145 // at the time of serialization.
146 mRestoredWithoutUids.clear();
147 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
148 tag = parser.getName();
149 if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
150 return;
151 }
152 if (type == XmlPullParser.START_TAG) {
153 if (TAG_PACKAGE.equals(tag)) {
154 int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
155 String name = parser.getAttributeValue(null, ATT_NAME);
156 if (!TextUtils.isEmpty(name)) {
157 if (forRestore) {
158 try {
159 //TODO: http://b/22388012
160 uid = mPm.getPackageUidAsUser(name,
161 UserHandle.USER_SYSTEM);
162 } catch (PackageManager.NameNotFoundException e) {
163 // noop
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400164 }
165 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400166
167 PackagePreferences r = getOrCreatePackagePreferences(name, uid,
168 XmlUtils.readIntAttribute(
169 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
170 XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
171 DEFAULT_PRIORITY),
172 XmlUtils.readIntAttribute(
173 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
174 XmlUtils.readBooleanAttribute(
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500175 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
176 XmlUtils.readBooleanAttribute(
177 parser, ATT_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400178 r.importance = XmlUtils.readIntAttribute(
179 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
180 r.priority = XmlUtils.readIntAttribute(
181 parser, ATT_PRIORITY, DEFAULT_PRIORITY);
182 r.visibility = XmlUtils.readIntAttribute(
183 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
184 r.showBadge = XmlUtils.readBooleanAttribute(
185 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
186 r.lockedAppFields = XmlUtils.readIntAttribute(parser,
187 ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
188
189 final int innerDepth = parser.getDepth();
190 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
191 && (type != XmlPullParser.END_TAG
192 || parser.getDepth() > innerDepth)) {
193 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
194 continue;
195 }
196
197 String tagName = parser.getName();
198 // Channel groups
199 if (TAG_GROUP.equals(tagName)) {
200 String id = parser.getAttributeValue(null, ATT_ID);
201 CharSequence groupName = parser.getAttributeValue(null,
202 ATT_NAME);
203 if (!TextUtils.isEmpty(id)) {
204 NotificationChannelGroup group
205 = new NotificationChannelGroup(id, groupName);
206 group.populateFromXml(parser);
207 r.groups.put(id, group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400208 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400209 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400210 // Channels
211 if (TAG_CHANNEL.equals(tagName)) {
212 String id = parser.getAttributeValue(null, ATT_ID);
213 String channelName = parser.getAttributeValue(null, ATT_NAME);
214 int channelImportance = XmlUtils.readIntAttribute(
215 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
216 if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
217 NotificationChannel channel = new NotificationChannel(id,
218 channelName, channelImportance);
219 if (forRestore) {
220 channel.populateFromXmlForRestore(parser, mContext);
221 } else {
222 channel.populateFromXml(parser);
223 }
224 r.channels.put(id, channel);
225 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400226 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400227 // Delegate
228 if (TAG_DELEGATE.equals(tagName)) {
229 int delegateId =
230 XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
231 String delegateName =
232 XmlUtils.readStringAttribute(parser, ATT_NAME);
233 boolean delegateEnabled = XmlUtils.readBooleanAttribute(
234 parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
235 boolean userAllowed = XmlUtils.readBooleanAttribute(
236 parser, ATT_USER_ALLOWED,
237 Delegate.DEFAULT_USER_ALLOWED);
238 Delegate d = null;
239 if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(
240 delegateName)) {
241 d = new Delegate(
242 delegateName, delegateId, delegateEnabled,
243 userAllowed);
244 }
245 r.delegate = d;
246 }
247
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400248 }
249
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400250 try {
251 deleteDefaultChannelIfNeeded(r);
252 } catch (PackageManager.NameNotFoundException e) {
253 Slog.e(TAG, "deleteDefaultChannelIfNeeded - Exception: " + e);
254 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400255 }
256 }
257 }
258 }
259 }
260 throw new IllegalStateException("Failed to reach END_DOCUMENT");
261 }
262
263 private PackagePreferences getPackagePreferences(String pkg, int uid) {
264 final String key = packagePreferencesKey(pkg, uid);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400265 synchronized (mPackagePreferences) {
266 return mPackagePreferences.get(key);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400267 }
268 }
269
270 private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) {
271 return getOrCreatePackagePreferences(pkg, uid,
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500272 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
273 DEFAULT_ALLOW_APP_OVERLAY);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400274 }
275
276 private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance,
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500277 int priority, int visibility, boolean showBadge, boolean allowAppOverlay) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400278 final String key = packagePreferencesKey(pkg, uid);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400279 synchronized (mPackagePreferences) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400280 PackagePreferences
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400281 r = (uid == UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400282 : mPackagePreferences.get(key);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400283 if (r == null) {
284 r = new PackagePreferences();
285 r.pkg = pkg;
286 r.uid = uid;
287 r.importance = importance;
288 r.priority = priority;
289 r.visibility = visibility;
290 r.showBadge = showBadge;
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500291 r.appOverlay = allowAppOverlay;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400292
293 try {
294 createDefaultChannelIfNeeded(r);
295 } catch (PackageManager.NameNotFoundException e) {
296 Slog.e(TAG, "createDefaultChannelIfNeeded - Exception: " + e);
297 }
298
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400299 if (r.uid == UNKNOWN_UID) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400300 mRestoredWithoutUids.put(pkg, r);
301 } else {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400302 mPackagePreferences.put(key, r);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400303 }
304 }
305 return r;
306 }
307 }
308
309 private boolean shouldHaveDefaultChannel(PackagePreferences r) throws
310 PackageManager.NameNotFoundException {
311 final int userId = UserHandle.getUserId(r.uid);
312 final ApplicationInfo applicationInfo =
313 mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
314 if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
315 // O apps should not have the default channel.
316 return false;
317 }
318
319 // Otherwise, this app should have the default channel.
320 return true;
321 }
322
323 private void deleteDefaultChannelIfNeeded(PackagePreferences r) throws
324 PackageManager.NameNotFoundException {
325 if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
326 // Not present
327 return;
328 }
329
330 if (shouldHaveDefaultChannel(r)) {
331 // Keep the default channel until upgraded.
332 return;
333 }
334
335 // Remove Default Channel.
336 r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
337 }
338
339 private void createDefaultChannelIfNeeded(PackagePreferences r) throws
340 PackageManager.NameNotFoundException {
341 if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
342 r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
343 com.android.internal.R.string.default_notification_channel_label));
344 return;
345 }
346
347 if (!shouldHaveDefaultChannel(r)) {
348 // Keep the default channel until upgraded.
349 return;
350 }
351
352 // Create Default Channel
353 NotificationChannel channel;
354 channel = new NotificationChannel(
355 NotificationChannel.DEFAULT_CHANNEL_ID,
356 mContext.getString(R.string.default_notification_channel_label),
357 r.importance);
358 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
359 channel.setLockscreenVisibility(r.visibility);
360 if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
361 channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
362 }
363 if (r.priority != DEFAULT_PRIORITY) {
364 channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
365 }
366 if (r.visibility != DEFAULT_VISIBILITY) {
367 channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
368 }
369 r.channels.put(channel.getId(), channel);
370 }
371
372 public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
373 out.startTag(null, TAG_RANKING);
374 out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
375
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400376 synchronized (mPackagePreferences) {
377 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400378 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400379 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400380 //TODO: http://b/22388012
381 if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_SYSTEM) {
382 continue;
383 }
384 final boolean hasNonDefaultSettings =
385 r.importance != DEFAULT_IMPORTANCE
386 || r.priority != DEFAULT_PRIORITY
387 || r.visibility != DEFAULT_VISIBILITY
388 || r.showBadge != DEFAULT_SHOW_BADGE
389 || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
390 || r.channels.size() > 0
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400391 || r.groups.size() > 0
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500392 || r.delegate != null
393 || r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400394 if (hasNonDefaultSettings) {
395 out.startTag(null, TAG_PACKAGE);
396 out.attribute(null, ATT_NAME, r.pkg);
397 if (r.importance != DEFAULT_IMPORTANCE) {
398 out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
399 }
400 if (r.priority != DEFAULT_PRIORITY) {
401 out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
402 }
403 if (r.visibility != DEFAULT_VISIBILITY) {
404 out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
405 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500406 if (r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY) {
407 out.attribute(null, ATT_APP_OVERLAY, Boolean.toString(r.appOverlay));
408 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400409 out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
410 out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
411 Integer.toString(r.lockedAppFields));
412
413 if (!forBackup) {
414 out.attribute(null, ATT_UID, Integer.toString(r.uid));
415 }
416
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400417 if (r.delegate != null) {
418 out.startTag(null, TAG_DELEGATE);
419
420 out.attribute(null, ATT_NAME, r.delegate.mPkg);
421 out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
422 if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
423 out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
424 }
425 if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
426 out.attribute(null, ATT_USER_ALLOWED,
427 Boolean.toString(r.delegate.mUserAllowed));
428 }
429 out.endTag(null, TAG_DELEGATE);
430 }
431
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400432 for (NotificationChannelGroup group : r.groups.values()) {
433 group.writeXml(out);
434 }
435
436 for (NotificationChannel channel : r.channels.values()) {
437 if (forBackup) {
438 if (!channel.isDeleted()) {
439 channel.writeXmlForBackup(out, mContext);
440 }
441 } else {
442 channel.writeXml(out);
443 }
444 }
445
446 out.endTag(null, TAG_PACKAGE);
447 }
448 }
449 }
450 out.endTag(null, TAG_RANKING);
451 }
452
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500453 public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
454 PackagePreferences p = getOrCreatePackagePreferences(pkg, uid);
455 p.appOverlay = allowed;
456 p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_APP_OVERLAY;
457 }
458
459 public boolean areAppOverlaysAllowed(String pkg, int uid) {
460 return getOrCreatePackagePreferences(pkg, uid).appOverlay;
461 }
462
463 public int getAppLockedFields(String pkg, int uid) {
464 return getOrCreatePackagePreferences(pkg, uid).lockedAppFields;
465 }
466
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400467 /**
468 * Gets importance.
469 */
470 @Override
471 public int getImportance(String packageName, int uid) {
472 return getOrCreatePackagePreferences(packageName, uid).importance;
473 }
474
475
476 /**
477 * Returns whether the importance of the corresponding notification is user-locked and shouldn't
478 * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
479 * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
480 */
481 public boolean getIsAppImportanceLocked(String packageName, int uid) {
482 int userLockedFields = getOrCreatePackagePreferences(packageName, uid).lockedAppFields;
483 return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
484 }
485
486 @Override
487 public boolean canShowBadge(String packageName, int uid) {
488 return getOrCreatePackagePreferences(packageName, uid).showBadge;
489 }
490
491 @Override
492 public void setShowBadge(String packageName, int uid, boolean showBadge) {
493 getOrCreatePackagePreferences(packageName, uid).showBadge = showBadge;
494 updateConfig();
495 }
496
497 @Override
498 public boolean isGroupBlocked(String packageName, int uid, String groupId) {
499 if (groupId == null) {
500 return false;
501 }
502 PackagePreferences r = getOrCreatePackagePreferences(packageName, uid);
503 NotificationChannelGroup group = r.groups.get(groupId);
504 if (group == null) {
505 return false;
506 }
507 return group.isBlocked();
508 }
509
510 int getPackagePriority(String pkg, int uid) {
511 return getOrCreatePackagePreferences(pkg, uid).priority;
512 }
513
514 int getPackageVisibility(String pkg, int uid) {
515 return getOrCreatePackagePreferences(pkg, uid).visibility;
516 }
517
518 @Override
519 public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
520 boolean fromTargetApp) {
521 Preconditions.checkNotNull(pkg);
522 Preconditions.checkNotNull(group);
523 Preconditions.checkNotNull(group.getId());
524 Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
525 PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
526 if (r == null) {
527 throw new IllegalArgumentException("Invalid package");
528 }
529 final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
530 if (!group.equals(oldGroup)) {
531 // will log for new entries as well as name/description changes
532 MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
533 }
534 if (oldGroup != null) {
535 group.setChannels(oldGroup.getChannels());
536
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -0400537 // apps can't update the blocked status or app overlay permission
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400538 if (fromTargetApp) {
539 group.setBlocked(oldGroup.isBlocked());
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -0400540 group.unlockFields(group.getUserLockedFields());
541 group.lockFields(oldGroup.getUserLockedFields());
542 } else {
543 // but the system can
544 if (group.isBlocked() != oldGroup.isBlocked()) {
545 group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
Beverly0479cde22018-11-09 11:05:34 -0500546 updateChannelsBypassingDnd(mContext.getUserId());
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -0400547 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400548 }
549 }
550 r.groups.put(group.getId(), group);
551 }
552
553 @Override
554 public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
555 boolean fromTargetApp, boolean hasDndAccess) {
556 Preconditions.checkNotNull(pkg);
557 Preconditions.checkNotNull(channel);
558 Preconditions.checkNotNull(channel.getId());
559 Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
560 PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
561 if (r == null) {
562 throw new IllegalArgumentException("Invalid package");
563 }
564 if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
565 throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
566 }
567 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
568 throw new IllegalArgumentException("Reserved id");
569 }
570 NotificationChannel existing = r.channels.get(channel.getId());
571 // Keep most of the existing settings
572 if (existing != null && fromTargetApp) {
573 if (existing.isDeleted()) {
574 existing.setDeleted(false);
575
576 // log a resurrected channel as if it's new again
577 MetricsLogger.action(getChannelLog(channel, pkg).setType(
578 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
579 }
580
581 existing.setName(channel.getName().toString());
582 existing.setDescription(channel.getDescription());
583 existing.setBlockableSystem(channel.isBlockableSystem());
584 if (existing.getGroup() == null) {
585 existing.setGroup(channel.getGroup());
586 }
587
588 // Apps are allowed to downgrade channel importance if the user has not changed any
589 // fields on this channel yet.
Beverly0479cde22018-11-09 11:05:34 -0500590 final int previousExistingImportance = existing.getImportance();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400591 if (existing.getUserLockedFields() == 0 &&
592 channel.getImportance() < existing.getImportance()) {
593 existing.setImportance(channel.getImportance());
594 }
595
596 // system apps and dnd access apps can bypass dnd if the user hasn't changed any
597 // fields on the channel yet
598 if (existing.getUserLockedFields() == 0 && hasDndAccess) {
599 boolean bypassDnd = channel.canBypassDnd();
600 existing.setBypassDnd(bypassDnd);
601
Beverly0479cde22018-11-09 11:05:34 -0500602 if (bypassDnd != mAreChannelsBypassingDnd
603 || previousExistingImportance != existing.getImportance()) {
604 updateChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400605 }
606 }
607
608 updateConfig();
609 return;
610 }
611 if (channel.getImportance() < IMPORTANCE_NONE
612 || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
613 throw new IllegalArgumentException("Invalid importance level");
614 }
615
616 // Reset fields that apps aren't allowed to set.
617 if (fromTargetApp && !hasDndAccess) {
618 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
619 }
620 if (fromTargetApp) {
621 channel.setLockscreenVisibility(r.visibility);
622 }
623 clearLockedFields(channel);
624 if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
625 channel.setLockscreenVisibility(
626 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
627 }
628 if (!r.showBadge) {
629 channel.setShowBadge(false);
630 }
631
632 r.channels.put(channel.getId(), channel);
633 if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
Beverly0479cde22018-11-09 11:05:34 -0500634 updateChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400635 }
636 MetricsLogger.action(getChannelLog(channel, pkg).setType(
637 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
638 }
639
640 void clearLockedFields(NotificationChannel channel) {
641 channel.unlockFields(channel.getUserLockedFields());
642 }
643
644 @Override
645 public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
646 boolean fromUser) {
647 Preconditions.checkNotNull(updatedChannel);
648 Preconditions.checkNotNull(updatedChannel.getId());
649 PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
650 if (r == null) {
651 throw new IllegalArgumentException("Invalid package");
652 }
653 NotificationChannel channel = r.channels.get(updatedChannel.getId());
654 if (channel == null || channel.isDeleted()) {
655 throw new IllegalArgumentException("Channel does not exist");
656 }
657 if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
658 updatedChannel.setLockscreenVisibility(
659 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
660 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400661 if (fromUser) {
662 updatedChannel.lockFields(channel.getUserLockedFields());
663 lockFieldsForUpdate(channel, updatedChannel);
Aaron Heuckroth9ae59f82018-07-13 12:23:36 -0400664 } else {
665 updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400666 }
667 r.channels.put(updatedChannel.getId(), updatedChannel);
668
Aaron Heuckroth9ae59f82018-07-13 12:23:36 -0400669 if (onlyHasDefaultChannel(pkg, uid)) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400670 // copy settings to app level so they are inherited by new channels
671 // when the app migrates
672 r.importance = updatedChannel.getImportance();
673 r.priority = updatedChannel.canBypassDnd()
674 ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
675 r.visibility = updatedChannel.getLockscreenVisibility();
676 r.showBadge = updatedChannel.canShowBadge();
677 }
678
679 if (!channel.equals(updatedChannel)) {
680 // only log if there are real changes
681 MetricsLogger.action(getChannelLog(updatedChannel, pkg));
682 }
683
Beverly0479cde22018-11-09 11:05:34 -0500684 if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
685 || channel.getImportance() != updatedChannel.getImportance()) {
686 updateChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400687 }
688 updateConfig();
689 }
690
691 @Override
692 public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
693 boolean includeDeleted) {
694 Preconditions.checkNotNull(pkg);
695 PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
696 if (r == null) {
697 return null;
698 }
699 if (channelId == null) {
700 channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
701 }
702 final NotificationChannel nc = r.channels.get(channelId);
703 if (nc != null && (includeDeleted || !nc.isDeleted())) {
704 return nc;
705 }
706 return null;
707 }
708
709 @Override
710 public void deleteNotificationChannel(String pkg, int uid, String channelId) {
711 PackagePreferences r = getPackagePreferences(pkg, uid);
712 if (r == null) {
713 return;
714 }
715 NotificationChannel channel = r.channels.get(channelId);
716 if (channel != null) {
717 channel.setDeleted(true);
718 LogMaker lm = getChannelLog(channel, pkg);
719 lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
720 MetricsLogger.action(lm);
721
722 if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -0500723 updateChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400724 }
725 }
726 }
727
728 @Override
729 @VisibleForTesting
730 public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
731 Preconditions.checkNotNull(pkg);
732 Preconditions.checkNotNull(channelId);
733 PackagePreferences r = getPackagePreferences(pkg, uid);
734 if (r == null) {
735 return;
736 }
737 r.channels.remove(channelId);
738 }
739
740 @Override
741 public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
742 Preconditions.checkNotNull(pkg);
743 PackagePreferences r = getPackagePreferences(pkg, uid);
744 if (r == null) {
745 return;
746 }
747 int N = r.channels.size() - 1;
748 for (int i = N; i >= 0; i--) {
749 String key = r.channels.keyAt(i);
750 if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
751 r.channels.remove(key);
752 }
753 }
754 }
755
756 public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
757 int uid, String groupId, boolean includeDeleted) {
758 Preconditions.checkNotNull(pkg);
759 PackagePreferences r = getPackagePreferences(pkg, uid);
760 if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
761 return null;
762 }
763 NotificationChannelGroup group = r.groups.get(groupId).clone();
764 group.setChannels(new ArrayList<>());
765 int N = r.channels.size();
766 for (int i = 0; i < N; i++) {
767 final NotificationChannel nc = r.channels.valueAt(i);
768 if (includeDeleted || !nc.isDeleted()) {
769 if (groupId.equals(nc.getGroup())) {
770 group.addChannel(nc);
771 }
772 }
773 }
774 return group;
775 }
776
777 public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
778 int uid) {
779 Preconditions.checkNotNull(pkg);
780 PackagePreferences r = getPackagePreferences(pkg, uid);
781 if (r == null) {
782 return null;
783 }
784 return r.groups.get(groupId);
785 }
786
787 @Override
788 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
Julia Reynolds13ed28b2018-09-21 15:20:13 -0400789 int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400790 Preconditions.checkNotNull(pkg);
791 Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
792 PackagePreferences r = getPackagePreferences(pkg, uid);
793 if (r == null) {
794 return ParceledListSlice.emptyList();
795 }
796 NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
797 int N = r.channels.size();
798 for (int i = 0; i < N; i++) {
799 final NotificationChannel nc = r.channels.valueAt(i);
800 if (includeDeleted || !nc.isDeleted()) {
801 if (nc.getGroup() != null) {
802 if (r.groups.get(nc.getGroup()) != null) {
803 NotificationChannelGroup ncg = groups.get(nc.getGroup());
804 if (ncg == null) {
805 ncg = r.groups.get(nc.getGroup()).clone();
806 ncg.setChannels(new ArrayList<>());
807 groups.put(nc.getGroup(), ncg);
808
809 }
810 ncg.addChannel(nc);
811 }
812 } else {
813 nonGrouped.addChannel(nc);
814 }
815 }
816 }
817 if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
818 groups.put(null, nonGrouped);
819 }
Julia Reynolds13ed28b2018-09-21 15:20:13 -0400820 if (includeEmpty) {
821 for (NotificationChannelGroup group : r.groups.values()) {
822 if (!groups.containsKey(group.getId())) {
823 groups.put(group.getId(), group);
824 }
825 }
826 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400827 return new ParceledListSlice<>(new ArrayList<>(groups.values()));
828 }
829
830 public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
831 String groupId) {
832 List<NotificationChannel> deletedChannels = new ArrayList<>();
833 PackagePreferences r = getPackagePreferences(pkg, uid);
834 if (r == null || TextUtils.isEmpty(groupId)) {
835 return deletedChannels;
836 }
837
838 r.groups.remove(groupId);
839
840 int N = r.channels.size();
841 for (int i = 0; i < N; i++) {
842 final NotificationChannel nc = r.channels.valueAt(i);
843 if (groupId.equals(nc.getGroup())) {
844 nc.setDeleted(true);
845 deletedChannels.add(nc);
846 }
847 }
848 return deletedChannels;
849 }
850
851 @Override
852 public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
853 int uid) {
854 PackagePreferences r = getPackagePreferences(pkg, uid);
855 if (r == null) {
856 return new ArrayList<>();
857 }
858 return r.groups.values();
859 }
860
861 @Override
862 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
863 boolean includeDeleted) {
864 Preconditions.checkNotNull(pkg);
865 List<NotificationChannel> channels = new ArrayList<>();
866 PackagePreferences r = getPackagePreferences(pkg, uid);
867 if (r == null) {
868 return ParceledListSlice.emptyList();
869 }
870 int N = r.channels.size();
871 for (int i = 0; i < N; i++) {
872 final NotificationChannel nc = r.channels.valueAt(i);
873 if (includeDeleted || !nc.isDeleted()) {
874 channels.add(nc);
875 }
876 }
877 return new ParceledListSlice<>(channels);
878 }
879
880 /**
Beverly0479cde22018-11-09 11:05:34 -0500881 * Gets all notification channels associated with the given pkg and userId that can bypass dnd
882 */
883 public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
884 int userId) {
885 List<NotificationChannel> channels = new ArrayList<>();
886 synchronized (mPackagePreferences) {
887 final PackagePreferences r = mPackagePreferences.get(
888 packagePreferencesKey(pkg, userId));
889 // notifications from this package aren't blocked
890 if (r != null && r.importance != IMPORTANCE_NONE) {
891 for (NotificationChannel channel : r.channels.values()) {
892 if (channelIsLive(r, channel) && channel.canBypassDnd()) {
893 channels.add(channel);
894 }
895 }
896 }
897 }
898 return new ParceledListSlice<>(channels);
899 }
900
901 /**
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400902 * True for pre-O apps that only have the default channel, or pre O apps that have no
903 * channels yet. This method will create the default channel for pre-O apps that don't have it.
904 * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
905 * upgrades.
906 */
907 public boolean onlyHasDefaultChannel(String pkg, int uid) {
908 PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
909 if (r.channels.size() == 1
910 && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
911 return true;
912 }
913 return false;
914 }
915
916 public int getDeletedChannelCount(String pkg, int uid) {
917 Preconditions.checkNotNull(pkg);
918 int deletedCount = 0;
919 PackagePreferences r = getPackagePreferences(pkg, uid);
920 if (r == null) {
921 return deletedCount;
922 }
923 int N = r.channels.size();
924 for (int i = 0; i < N; i++) {
925 final NotificationChannel nc = r.channels.valueAt(i);
926 if (nc.isDeleted()) {
927 deletedCount++;
928 }
929 }
930 return deletedCount;
931 }
932
933 public int getBlockedChannelCount(String pkg, int uid) {
934 Preconditions.checkNotNull(pkg);
935 int blockedCount = 0;
936 PackagePreferences r = getPackagePreferences(pkg, uid);
937 if (r == null) {
938 return blockedCount;
939 }
940 int N = r.channels.size();
941 for (int i = 0; i < N; i++) {
942 final NotificationChannel nc = r.channels.valueAt(i);
943 if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
944 blockedCount++;
945 }
946 }
947 return blockedCount;
948 }
949
950 public int getBlockedAppCount(int userId) {
951 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400952 synchronized (mPackagePreferences) {
953 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400954 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400955 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400956 if (userId == UserHandle.getUserId(r.uid)
957 && r.importance == IMPORTANCE_NONE) {
958 count++;
959 }
960 }
961 }
962 return count;
963 }
964
Beverly0479cde22018-11-09 11:05:34 -0500965 /**
966 * Returns the number of apps that have at least one notification channel that can bypass DND
967 * for given particular user
968 */
969 public int getAppsBypassingDndCount(int userId) {
970 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400971 synchronized (mPackagePreferences) {
Beverly0479cde22018-11-09 11:05:34 -0500972 final int numPackagePreferences = mPackagePreferences.size();
973 for (int i = 0; i < numPackagePreferences; i++) {
974 final PackagePreferences r = mPackagePreferences.valueAt(i);
975 // Package isn't associated with this userId or notifications from this package are
976 // blocked
977 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
978 continue;
979 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400980
Beverly0479cde22018-11-09 11:05:34 -0500981 for (NotificationChannel channel : r.channels.values()) {
982 if (channelIsLive(r, channel) && channel.canBypassDnd()) {
983 count++;
984 break;
985 }
986 }
987 }
988 }
989 return count;
990 }
991
992 /**
993 * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
994 * updating
995 * @param userId
996 */
997 private void syncChannelsBypassingDnd(int userId) {
998 mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
999 & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
1000 updateChannelsBypassingDnd(userId);
1001 }
1002
1003 /**
1004 * Updates the user's NotificationPolicy based on whether the given userId
1005 * has channels bypassing DND
1006 * @param userId
1007 */
1008 private void updateChannelsBypassingDnd(int userId) {
1009 synchronized (mPackagePreferences) {
1010 final int numPackagePreferences = mPackagePreferences.size();
1011 for (int i = 0; i < numPackagePreferences; i++) {
1012 final PackagePreferences r = mPackagePreferences.valueAt(i);
1013 // Package isn't associated with this userId or notifications from this package are
1014 // blocked
1015 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1016 continue;
1017 }
1018
1019 for (NotificationChannel channel : r.channels.values()) {
1020 if (channelIsLive(r, channel) && channel.canBypassDnd()) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001021 if (!mAreChannelsBypassingDnd) {
1022 mAreChannelsBypassingDnd = true;
1023 updateZenPolicy(true);
1024 }
1025 return;
1026 }
1027 }
1028 }
1029 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001030 // If no channels bypass DND, update the zen policy once to disable DND bypass.
1031 if (mAreChannelsBypassingDnd) {
1032 mAreChannelsBypassingDnd = false;
1033 updateZenPolicy(false);
1034 }
1035 }
1036
Beverly0479cde22018-11-09 11:05:34 -05001037 private boolean channelIsLive(PackagePreferences pkgPref, NotificationChannel channel) {
1038 // Channel is in a group that's blocked
Beverly4f7b53d2018-11-20 09:56:31 -05001039 if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
1040 return false;
Beverly0479cde22018-11-09 11:05:34 -05001041 }
1042
1043 // Channel is deleted or is blocked
1044 if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
1045 return false;
1046 }
1047
1048 return true;
1049 }
1050
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001051 public void updateZenPolicy(boolean areChannelsBypassingDnd) {
1052 NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
1053 mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
1054 policy.priorityCategories, policy.priorityCallSenders,
1055 policy.priorityMessageSenders, policy.suppressedVisualEffects,
1056 (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
1057 : 0)));
1058 }
1059
1060 public boolean areChannelsBypassingDnd() {
1061 return mAreChannelsBypassingDnd;
1062 }
1063
1064 /**
1065 * Sets importance.
1066 */
1067 @Override
1068 public void setImportance(String pkgName, int uid, int importance) {
1069 getOrCreatePackagePreferences(pkgName, uid).importance = importance;
1070 updateConfig();
1071 }
1072
1073 public void setEnabled(String packageName, int uid, boolean enabled) {
1074 boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
1075 if (wasEnabled == enabled) {
1076 return;
1077 }
1078 setImportance(packageName, uid,
1079 enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
1080 }
1081
1082 /**
1083 * Sets whether any notifications from the app, represented by the given {@code pkgName} and
1084 * {@code uid}, have their importance locked by the user. Locked notifications don't get
1085 * considered for sentiment adjustments (and thus never show a blocking helper).
1086 */
1087 public void setAppImportanceLocked(String packageName, int uid) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001088 PackagePreferences prefs = getOrCreatePackagePreferences(packageName, uid);
1089 if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001090 return;
1091 }
1092
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001093 prefs.lockedAppFields = prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001094 updateConfig();
1095 }
1096
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001097 /**
1098 * Returns the delegate for a given package, if it's allowed by the package and the user.
1099 */
1100 public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
1101 PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
1102
1103 if (prefs == null || prefs.delegate == null) {
1104 return null;
1105 }
1106 if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
1107 return null;
1108 }
1109 return prefs.delegate.mPkg;
1110 }
1111
1112 /**
1113 * Used by an app to delegate notification posting privileges to another apps.
1114 */
1115 public void setNotificationDelegate(String sourcePkg, int sourceUid,
1116 String delegatePkg, int delegateUid) {
1117 PackagePreferences prefs = getOrCreatePackagePreferences(sourcePkg, sourceUid);
1118
1119 boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
1120 Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
1121 prefs.delegate = delegate;
1122 updateConfig();
1123 }
1124
1125 /**
1126 * Used by an app to turn off its notification delegate.
1127 */
1128 public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
1129 PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
1130 if (prefs != null && prefs.delegate != null) {
1131 prefs.delegate.mEnabled = false;
1132 updateConfig();
1133 }
1134 }
1135
1136 /**
1137 * Toggles whether an app can have a notification delegate on behalf of a user.
1138 */
1139 public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
1140 PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
1141 if (prefs != null && prefs.delegate != null) {
1142 prefs.delegate.mUserAllowed = userAllowed;
1143 updateConfig();
1144 }
1145 }
1146
1147 /**
1148 * Returns whether the given app is allowed on post notifications on behalf of the other given
1149 * app.
1150 */
1151 public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
1152 String potentialDelegatePkg, int potentialDelegateUid) {
1153 PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
1154
1155 return prefs != null && prefs.isValidDelegate(potentialDelegatePkg, potentialDelegateUid);
1156 }
1157
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001158 @VisibleForTesting
1159 void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
1160 if (original.canBypassDnd() != update.canBypassDnd()) {
1161 update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
1162 }
1163 if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
1164 update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
1165 }
1166 if (original.getImportance() != update.getImportance()) {
1167 update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
1168 }
1169 if (original.shouldShowLights() != update.shouldShowLights()
1170 || original.getLightColor() != update.getLightColor()) {
1171 update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
1172 }
1173 if (!Objects.equals(original.getSound(), update.getSound())) {
1174 update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
1175 }
1176 if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
1177 || original.shouldVibrate() != update.shouldVibrate()) {
1178 update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
1179 }
1180 if (original.canShowBadge() != update.canShowBadge()) {
1181 update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
1182 }
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -04001183 if (original.isAppOverlayAllowed() != update.isAppOverlayAllowed()) {
1184 update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY);
1185 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001186 }
1187
1188 public void dump(PrintWriter pw, String prefix,
1189 @NonNull NotificationManagerService.DumpFilter filter) {
1190 pw.print(prefix);
1191 pw.println("per-package config:");
1192
1193 pw.println("PackagePreferencess:");
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001194 synchronized (mPackagePreferences) {
1195 dumpPackagePreferencess(pw, prefix, filter, mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001196 }
1197 pw.println("Restored without uid:");
1198 dumpPackagePreferencess(pw, prefix, filter, mRestoredWithoutUids);
1199 }
1200
1201 public void dump(ProtoOutputStream proto,
1202 @NonNull NotificationManagerService.DumpFilter filter) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001203 synchronized (mPackagePreferences) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001204 dumpPackagePreferencess(proto, RankingHelperProto.RECORDS, filter,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001205 mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001206 }
1207 dumpPackagePreferencess(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
1208 mRestoredWithoutUids);
1209 }
1210
1211 private static void dumpPackagePreferencess(PrintWriter pw, String prefix,
1212 @NonNull NotificationManagerService.DumpFilter filter,
1213 ArrayMap<String, PackagePreferences> PackagePreferencess) {
1214 final int N = PackagePreferencess.size();
1215 for (int i = 0; i < N; i++) {
1216 final PackagePreferences r = PackagePreferencess.valueAt(i);
1217 if (filter.matches(r.pkg)) {
1218 pw.print(prefix);
1219 pw.print(" AppSettings: ");
1220 pw.print(r.pkg);
1221 pw.print(" (");
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001222 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001223 pw.print(')');
1224 if (r.importance != DEFAULT_IMPORTANCE) {
1225 pw.print(" importance=");
1226 pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
1227 }
1228 if (r.priority != DEFAULT_PRIORITY) {
1229 pw.print(" priority=");
1230 pw.print(Notification.priorityToString(r.priority));
1231 }
1232 if (r.visibility != DEFAULT_VISIBILITY) {
1233 pw.print(" visibility=");
1234 pw.print(Notification.visibilityToString(r.visibility));
1235 }
1236 pw.print(" showBadge=");
1237 pw.print(Boolean.toString(r.showBadge));
1238 pw.println();
1239 for (NotificationChannel channel : r.channels.values()) {
1240 pw.print(prefix);
1241 channel.dump(pw, " ", filter.redact);
1242 }
1243 for (NotificationChannelGroup group : r.groups.values()) {
1244 pw.print(prefix);
1245 pw.print(" ");
1246 pw.print(" ");
1247 pw.println(group);
1248 }
1249 }
1250 }
1251 }
1252
1253 private static void dumpPackagePreferencess(ProtoOutputStream proto, long fieldId,
1254 @NonNull NotificationManagerService.DumpFilter filter,
1255 ArrayMap<String, PackagePreferences> PackagePreferencess) {
1256 final int N = PackagePreferencess.size();
1257 long fToken;
1258 for (int i = 0; i < N; i++) {
1259 final PackagePreferences r = PackagePreferencess.valueAt(i);
1260 if (filter.matches(r.pkg)) {
1261 fToken = proto.start(fieldId);
1262
1263 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
1264 proto.write(RankingHelperProto.RecordProto.UID, r.uid);
1265 proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
1266 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
1267 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
1268 proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
1269
1270 for (NotificationChannel channel : r.channels.values()) {
1271 channel.writeToProto(proto, RankingHelperProto.RecordProto.CHANNELS);
1272 }
1273 for (NotificationChannelGroup group : r.groups.values()) {
1274 group.writeToProto(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS);
1275 }
1276
1277 proto.end(fToken);
1278 }
1279 }
1280 }
1281
1282 public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
1283 JSONObject ranking = new JSONObject();
1284 JSONArray PackagePreferencess = new JSONArray();
1285 try {
1286 ranking.put("noUid", mRestoredWithoutUids.size());
1287 } catch (JSONException e) {
1288 // pass
1289 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001290 synchronized (mPackagePreferences) {
1291 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001292 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001293 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001294 if (filter == null || filter.matches(r.pkg)) {
1295 JSONObject PackagePreferences = new JSONObject();
1296 try {
1297 PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
1298 PackagePreferences.put("packageName", r.pkg);
1299 if (r.importance != DEFAULT_IMPORTANCE) {
1300 PackagePreferences.put("importance",
1301 NotificationListenerService.Ranking.importanceToString(
1302 r.importance));
1303 }
1304 if (r.priority != DEFAULT_PRIORITY) {
1305 PackagePreferences.put("priority",
1306 Notification.priorityToString(r.priority));
1307 }
1308 if (r.visibility != DEFAULT_VISIBILITY) {
1309 PackagePreferences.put("visibility",
1310 Notification.visibilityToString(r.visibility));
1311 }
1312 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1313 PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
1314 }
1315 JSONArray channels = new JSONArray();
1316 for (NotificationChannel channel : r.channels.values()) {
1317 channels.put(channel.toJson());
1318 }
1319 PackagePreferences.put("channels", channels);
1320 JSONArray groups = new JSONArray();
1321 for (NotificationChannelGroup group : r.groups.values()) {
1322 groups.put(group.toJson());
1323 }
1324 PackagePreferences.put("groups", groups);
1325 } catch (JSONException e) {
1326 // pass
1327 }
1328 PackagePreferencess.put(PackagePreferences);
1329 }
1330 }
1331 }
1332 try {
1333 ranking.put("PackagePreferencess", PackagePreferencess);
1334 } catch (JSONException e) {
1335 // pass
1336 }
1337 return ranking;
1338 }
1339
1340 /**
1341 * Dump only the ban information as structured JSON for the stats collector.
1342 *
1343 * This is intentionally redundant with {#link dumpJson} because the old
1344 * scraper will expect this format.
1345 *
1346 * @param filter
1347 * @return
1348 */
1349 public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
1350 JSONArray bans = new JSONArray();
1351 Map<Integer, String> packageBans = getPackageBans();
1352 for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
1353 final int userId = UserHandle.getUserId(ban.getKey());
1354 final String packageName = ban.getValue();
1355 if (filter == null || filter.matches(packageName)) {
1356 JSONObject banJson = new JSONObject();
1357 try {
1358 banJson.put("userId", userId);
1359 banJson.put("packageName", packageName);
1360 } catch (JSONException e) {
1361 e.printStackTrace();
1362 }
1363 bans.put(banJson);
1364 }
1365 }
1366 return bans;
1367 }
1368
1369 public Map<Integer, String> getPackageBans() {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001370 synchronized (mPackagePreferences) {
1371 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001372 ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
1373 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001374 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001375 if (r.importance == IMPORTANCE_NONE) {
1376 packageBans.put(r.uid, r.pkg);
1377 }
1378 }
1379
1380 return packageBans;
1381 }
1382 }
1383
1384 /**
1385 * Dump only the channel information as structured JSON for the stats collector.
1386 *
1387 * This is intentionally redundant with {#link dumpJson} because the old
1388 * scraper will expect this format.
1389 *
1390 * @param filter
1391 * @return
1392 */
1393 public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
1394 JSONArray channels = new JSONArray();
1395 Map<String, Integer> packageChannels = getPackageChannels();
1396 for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) {
1397 final String packageName = channelCount.getKey();
1398 if (filter == null || filter.matches(packageName)) {
1399 JSONObject channelCountJson = new JSONObject();
1400 try {
1401 channelCountJson.put("packageName", packageName);
1402 channelCountJson.put("channelCount", channelCount.getValue());
1403 } catch (JSONException e) {
1404 e.printStackTrace();
1405 }
1406 channels.put(channelCountJson);
1407 }
1408 }
1409 return channels;
1410 }
1411
1412 private Map<String, Integer> getPackageChannels() {
1413 ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001414 synchronized (mPackagePreferences) {
1415 for (int i = 0; i < mPackagePreferences.size(); i++) {
1416 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001417 int channelCount = 0;
1418 for (int j = 0; j < r.channels.size(); j++) {
1419 if (!r.channels.valueAt(j).isDeleted()) {
1420 channelCount++;
1421 }
1422 }
1423 packageChannels.put(r.pkg, channelCount);
1424 }
1425 }
1426 return packageChannels;
1427 }
1428
Beverly0479cde22018-11-09 11:05:34 -05001429 /**
1430 * Called when user switches
1431 */
1432 public void onUserSwitched(int userId) {
1433 syncChannelsBypassingDnd(userId);
1434 }
1435
1436 /**
1437 * Called when user is unlocked
1438 */
1439 public void onUserUnlocked(int userId) {
1440 syncChannelsBypassingDnd(userId);
1441 }
1442
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001443 public void onUserRemoved(int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001444 synchronized (mPackagePreferences) {
1445 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001446 for (int i = N - 1; i >= 0; i--) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001447 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001448 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001449 mPackagePreferences.removeAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001450 }
1451 }
1452 }
1453 }
1454
1455 protected void onLocaleChanged(Context context, int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001456 synchronized (mPackagePreferences) {
1457 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001458 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001459 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001460 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
1461 if (PackagePreferences.channels.containsKey(
1462 NotificationChannel.DEFAULT_CHANNEL_ID)) {
1463 PackagePreferences.channels.get(
1464 NotificationChannel.DEFAULT_CHANNEL_ID).setName(
1465 context.getResources().getString(
1466 R.string.default_notification_channel_label));
1467 }
1468 }
1469 }
1470 }
1471 }
1472
1473 public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
1474 int[] uidList) {
1475 if (pkgList == null || pkgList.length == 0) {
1476 return; // nothing to do
1477 }
1478 boolean updated = false;
1479 if (removingPackage) {
1480 // Remove notification settings for uninstalled package
1481 int size = Math.min(pkgList.length, uidList.length);
1482 for (int i = 0; i < size; i++) {
1483 final String pkg = pkgList[i];
1484 final int uid = uidList[i];
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001485 synchronized (mPackagePreferences) {
1486 mPackagePreferences.remove(packagePreferencesKey(pkg, uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001487 }
1488 mRestoredWithoutUids.remove(pkg);
1489 updated = true;
1490 }
1491 } else {
1492 for (String pkg : pkgList) {
1493 // Package install
1494 final PackagePreferences r = mRestoredWithoutUids.get(pkg);
1495 if (r != null) {
1496 try {
1497 r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
1498 mRestoredWithoutUids.remove(pkg);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001499 synchronized (mPackagePreferences) {
1500 mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001501 }
1502 updated = true;
1503 } catch (PackageManager.NameNotFoundException e) {
1504 // noop
1505 }
1506 }
1507 // Package upgrade
1508 try {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001509 synchronized (mPackagePreferences) {
1510 PackagePreferences fullPackagePreferences = getPackagePreferences(pkg,
1511 mPm.getPackageUidAsUser(pkg, changeUserId));
1512 if (fullPackagePreferences != null) {
1513 createDefaultChannelIfNeeded(fullPackagePreferences);
1514 deleteDefaultChannelIfNeeded(fullPackagePreferences);
1515 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001516 }
1517 } catch (PackageManager.NameNotFoundException e) {
1518 }
1519 }
1520 }
1521
1522 if (updated) {
1523 updateConfig();
1524 }
1525 }
1526
1527 private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
1528 return new LogMaker(
1529 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1530 .ACTION_NOTIFICATION_CHANNEL)
1531 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1532 .setPackageName(pkg)
1533 .addTaggedData(
1534 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1535 .FIELD_NOTIFICATION_CHANNEL_ID,
1536 channel.getId())
1537 .addTaggedData(
1538 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1539 .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
1540 channel.getImportance());
1541 }
1542
1543 private LogMaker getChannelGroupLog(String groupId, String pkg) {
1544 return new LogMaker(
1545 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1546 .ACTION_NOTIFICATION_CHANNEL_GROUP)
1547 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1548 .addTaggedData(
1549 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1550 .FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
1551 groupId)
1552 .setPackageName(pkg);
1553 }
1554
1555
1556 public void updateBadgingEnabled() {
1557 if (mBadgingEnabled == null) {
1558 mBadgingEnabled = new SparseBooleanArray();
1559 }
1560 boolean changed = false;
1561 // update the cached values
1562 for (int index = 0; index < mBadgingEnabled.size(); index++) {
1563 int userId = mBadgingEnabled.keyAt(index);
1564 final boolean oldValue = mBadgingEnabled.get(userId);
1565 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1566 Settings.Secure.NOTIFICATION_BADGING,
1567 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
1568 mBadgingEnabled.put(userId, newValue);
1569 changed |= oldValue != newValue;
1570 }
1571 if (changed) {
1572 updateConfig();
1573 }
1574 }
1575
1576 public boolean badgingEnabled(UserHandle userHandle) {
1577 int userId = userHandle.getIdentifier();
1578 if (userId == UserHandle.USER_ALL) {
1579 return false;
1580 }
1581 if (mBadgingEnabled.indexOfKey(userId) < 0) {
1582 mBadgingEnabled.put(userId,
1583 Settings.Secure.getIntForUser(mContext.getContentResolver(),
1584 Settings.Secure.NOTIFICATION_BADGING,
1585 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
1586 }
1587 return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
1588 }
1589
1590 private void updateConfig() {
1591 mRankingHandler.requestSort();
1592 }
1593
1594 private static String packagePreferencesKey(String pkg, int uid) {
1595 return pkg + "|" + uid;
1596 }
1597
1598 private static class PackagePreferences {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001599 String pkg;
1600 int uid = UNKNOWN_UID;
1601 int importance = DEFAULT_IMPORTANCE;
1602 int priority = DEFAULT_PRIORITY;
1603 int visibility = DEFAULT_VISIBILITY;
1604 boolean showBadge = DEFAULT_SHOW_BADGE;
Julia Reynolds33ab8a02018-12-17 16:19:52 -05001605 boolean appOverlay = DEFAULT_ALLOW_APP_OVERLAY;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001606 int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
1607
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001608 Delegate delegate = null;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001609 ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
1610 Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001611
1612 public boolean isValidDelegate(String pkg, int uid) {
1613 return delegate != null && delegate.isAllowed(pkg, uid);
1614 }
1615 }
1616
1617 private static class Delegate {
1618 static final boolean DEFAULT_ENABLED = true;
1619 static final boolean DEFAULT_USER_ALLOWED = true;
1620 String mPkg;
1621 int mUid = UNKNOWN_UID;
1622 boolean mEnabled = DEFAULT_ENABLED;
1623 boolean mUserAllowed = DEFAULT_USER_ALLOWED;
1624
1625 Delegate(String pkg, int uid, boolean enabled, boolean userAllowed) {
1626 mPkg = pkg;
1627 mUid = uid;
1628 mEnabled = enabled;
1629 mUserAllowed = userAllowed;
1630 }
1631
1632 public boolean isAllowed(String pkg, int uid) {
1633 if (pkg == null || uid == UNKNOWN_UID) {
1634 return false;
1635 }
1636 return pkg.equals(mPkg)
1637 && uid == mUid
1638 && (mUserAllowed && mEnabled);
1639 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001640 }
1641}