blob: 9e16632bb6c4758d1670a825b2df389eb01f647b [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;
Julia Reynolds0c245002019-03-27 16:10:11 -040040import android.util.ArraySet;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040041import android.util.Slog;
42import android.util.SparseBooleanArray;
43import android.util.proto.ProtoOutputStream;
44
45import com.android.internal.R;
46import com.android.internal.annotations.VisibleForTesting;
47import com.android.internal.logging.MetricsLogger;
48import com.android.internal.util.Preconditions;
49import com.android.internal.util.XmlUtils;
50
51import org.json.JSONArray;
52import org.json.JSONException;
53import org.json.JSONObject;
54import org.xmlpull.v1.XmlPullParser;
55import org.xmlpull.v1.XmlPullParserException;
56import org.xmlpull.v1.XmlSerializer;
57
58import java.io.IOException;
59import java.io.PrintWriter;
60import java.util.ArrayList;
61import java.util.Arrays;
62import java.util.Collection;
63import java.util.List;
64import java.util.Map;
65import java.util.Objects;
66import java.util.concurrent.ConcurrentHashMap;
67
68public class PreferencesHelper implements RankingConfig {
69 private static final String TAG = "NotificationPrefHelper";
70 private static final int XML_VERSION = 1;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040071 private static final int UNKNOWN_UID = UserHandle.USER_NULL;
Julia Reynolds413ba842019-01-11 10:38:08 -050072 private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040073
74 @VisibleForTesting
75 static final String TAG_RANKING = "ranking";
76 private static final String TAG_PACKAGE = "package";
77 private static final String TAG_CHANNEL = "channel";
78 private static final String TAG_GROUP = "channelGroup";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040079 private static final String TAG_DELEGATE = "delegate";
Julia Reynolds12ad7ca2019-01-28 09:29:16 -050080 private static final String TAG_STATUS_ICONS = "status_icons";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040081
82 private static final String ATT_VERSION = "version";
83 private static final String ATT_NAME = "name";
84 private static final String ATT_UID = "uid";
85 private static final String ATT_ID = "id";
Mady Mellorc39b4ae2019-01-09 17:11:37 -080086 private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040087 private static final String ATT_PRIORITY = "priority";
88 private static final String ATT_VISIBILITY = "visibility";
89 private static final String ATT_IMPORTANCE = "importance";
90 private static final String ATT_SHOW_BADGE = "show_badge";
91 private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040092 private static final String ATT_ENABLED = "enabled";
93 private static final String ATT_USER_ALLOWED = "allowed";
Julia Reynolds12ad7ca2019-01-28 09:29:16 -050094 private static final String ATT_HIDE_SILENT = "hide_silent";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040095
96 private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
97 private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
98 private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
Julia Reynolds12ad7ca2019-01-28 09:29:16 -050099 @VisibleForTesting
Julia Reynolds2594b472019-04-03 13:30:16 -0400100 static final boolean DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS = true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400101 private static final boolean DEFAULT_SHOW_BADGE = true;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800102 private static final boolean DEFAULT_ALLOW_BUBBLE = true;
Julia Reynolds413ba842019-01-11 10:38:08 -0500103 private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false;
Julia Reynolds0c245002019-03-27 16:10:11 -0400104 private static final boolean DEFAULT_APP_LOCKED_IMPORTANCE = false;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800105
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400106 /**
107 * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
108 * fields.
109 */
110 private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
111
112 /**
113 * All user-lockable fields for a given application.
114 */
115 @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
116 public @interface LockableAppFields {
117 int USER_LOCKED_IMPORTANCE = 0x00000001;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800118 int USER_LOCKED_BUBBLE = 0x00000002;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400119 }
120
121 // pkg|uid => PackagePreferences
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400122 private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400123 // pkg => PackagePreferences
124 private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
125
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400126 private final Context mContext;
127 private final PackageManager mPm;
128 private final RankingHandler mRankingHandler;
129 private final ZenModeHelper mZenModeHelper;
130
131 private SparseBooleanArray mBadgingEnabled;
Julia Reynolds4509ce72019-01-31 13:12:43 -0500132 private SparseBooleanArray mBubblesEnabled;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400133 private boolean mAreChannelsBypassingDnd;
Julia Reynolds2594b472019-04-03 13:30:16 -0400134 private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400135
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400136 public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
137 ZenModeHelper zenHelper) {
138 mContext = context;
139 mZenModeHelper = zenHelper;
140 mRankingHandler = rankingHandler;
141 mPm = pm;
142
143 updateBadgingEnabled();
Julia Reynolds4509ce72019-01-31 13:12:43 -0500144 updateBubblesEnabled();
Beverly0479cde22018-11-09 11:05:34 -0500145 syncChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400146 }
147
Annie Meng8b646fd2019-02-01 18:46:42 +0000148 public void readXml(XmlPullParser parser, boolean forRestore, int userId)
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400149 throws XmlPullParserException, IOException {
150 int type = parser.getEventType();
151 if (type != XmlPullParser.START_TAG) return;
152 String tag = parser.getName();
153 if (!TAG_RANKING.equals(tag)) return;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400154 synchronized (mPackagePreferences) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500155 // Clobber groups and channels with the xml, but don't delete other data that wasn't
156 // present at the time of serialization.
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400157 mRestoredWithoutUids.clear();
158 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
159 tag = parser.getName();
160 if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
161 return;
162 }
163 if (type == XmlPullParser.START_TAG) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500164 if (TAG_STATUS_ICONS.equals(tag)) {
Annie Meng8b646fd2019-02-01 18:46:42 +0000165 if (forRestore && userId != UserHandle.USER_SYSTEM) {
166 continue;
167 }
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500168 mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute(
169 parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
170 } else if (TAG_PACKAGE.equals(tag)) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400171 int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
172 String name = parser.getAttributeValue(null, ATT_NAME);
173 if (!TextUtils.isEmpty(name)) {
174 if (forRestore) {
175 try {
Annie Meng8b646fd2019-02-01 18:46:42 +0000176 uid = mPm.getPackageUidAsUser(name, userId);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400177 } catch (PackageManager.NameNotFoundException e) {
178 // noop
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400179 }
180 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400181
Julia Reynolds5c399c62019-04-08 14:42:53 -0400182 PackagePreferences r = getOrCreatePackagePreferencesLocked(name, uid,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400183 XmlUtils.readIntAttribute(
184 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
185 XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
186 DEFAULT_PRIORITY),
187 XmlUtils.readIntAttribute(
188 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
189 XmlUtils.readBooleanAttribute(
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500190 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
191 XmlUtils.readBooleanAttribute(
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800192 parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400193 r.importance = XmlUtils.readIntAttribute(
194 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
195 r.priority = XmlUtils.readIntAttribute(
196 parser, ATT_PRIORITY, DEFAULT_PRIORITY);
197 r.visibility = XmlUtils.readIntAttribute(
198 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
199 r.showBadge = XmlUtils.readBooleanAttribute(
200 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
201 r.lockedAppFields = XmlUtils.readIntAttribute(parser,
202 ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
203
204 final int innerDepth = parser.getDepth();
205 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
206 && (type != XmlPullParser.END_TAG
207 || parser.getDepth() > innerDepth)) {
208 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
209 continue;
210 }
211
212 String tagName = parser.getName();
213 // Channel groups
214 if (TAG_GROUP.equals(tagName)) {
215 String id = parser.getAttributeValue(null, ATT_ID);
216 CharSequence groupName = parser.getAttributeValue(null,
217 ATT_NAME);
218 if (!TextUtils.isEmpty(id)) {
219 NotificationChannelGroup group
220 = new NotificationChannelGroup(id, groupName);
221 group.populateFromXml(parser);
222 r.groups.put(id, group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400223 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400224 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400225 // Channels
226 if (TAG_CHANNEL.equals(tagName)) {
227 String id = parser.getAttributeValue(null, ATT_ID);
228 String channelName = parser.getAttributeValue(null, ATT_NAME);
229 int channelImportance = XmlUtils.readIntAttribute(
230 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
231 if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
232 NotificationChannel channel = new NotificationChannel(id,
233 channelName, channelImportance);
234 if (forRestore) {
235 channel.populateFromXmlForRestore(parser, mContext);
236 } else {
237 channel.populateFromXml(parser);
238 }
239 r.channels.put(id, channel);
240 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400241 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400242 // Delegate
243 if (TAG_DELEGATE.equals(tagName)) {
244 int delegateId =
245 XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
246 String delegateName =
247 XmlUtils.readStringAttribute(parser, ATT_NAME);
248 boolean delegateEnabled = XmlUtils.readBooleanAttribute(
249 parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
250 boolean userAllowed = XmlUtils.readBooleanAttribute(
251 parser, ATT_USER_ALLOWED,
252 Delegate.DEFAULT_USER_ALLOWED);
253 Delegate d = null;
254 if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(
255 delegateName)) {
256 d = new Delegate(
257 delegateName, delegateId, delegateEnabled,
258 userAllowed);
259 }
260 r.delegate = d;
261 }
262
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400263 }
264
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400265 try {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400266 deleteDefaultChannelIfNeededLocked(r);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400267 } catch (PackageManager.NameNotFoundException e) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400268 Slog.e(TAG, "deleteDefaultChannelIfNeededLocked - Exception: " + e);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400269 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400270 }
271 }
272 }
273 }
274 }
275 throw new IllegalStateException("Failed to reach END_DOCUMENT");
276 }
277
Julia Reynolds5c399c62019-04-08 14:42:53 -0400278 private PackagePreferences getPackagePreferencesLocked(String pkg, int uid) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400279 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400280 return mPackagePreferences.get(key);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400281 }
282
Julia Reynolds5c399c62019-04-08 14:42:53 -0400283 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, int uid) {
284 return getOrCreatePackagePreferencesLocked(pkg, uid,
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500285 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800286 DEFAULT_ALLOW_BUBBLE);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400287 }
288
Julia Reynolds5c399c62019-04-08 14:42:53 -0400289 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, int uid,
290 int importance, int priority, int visibility, boolean showBadge, boolean allowBubble) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400291 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400292 PackagePreferences
293 r = (uid == UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
294 : mPackagePreferences.get(key);
295 if (r == null) {
296 r = new PackagePreferences();
297 r.pkg = pkg;
298 r.uid = uid;
299 r.importance = importance;
300 r.priority = priority;
301 r.visibility = visibility;
302 r.showBadge = showBadge;
303 r.allowBubble = allowBubble;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400304
Julia Reynolds5c399c62019-04-08 14:42:53 -0400305 try {
306 createDefaultChannelIfNeededLocked(r);
307 } catch (PackageManager.NameNotFoundException e) {
308 Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400309 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400310
311 if (r.uid == UNKNOWN_UID) {
312 mRestoredWithoutUids.put(pkg, r);
313 } else {
314 mPackagePreferences.put(key, r);
315 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400316 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400317 return r;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400318 }
319
320 private boolean shouldHaveDefaultChannel(PackagePreferences r) throws
321 PackageManager.NameNotFoundException {
322 final int userId = UserHandle.getUserId(r.uid);
323 final ApplicationInfo applicationInfo =
324 mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
325 if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
326 // O apps should not have the default channel.
327 return false;
328 }
329
330 // Otherwise, this app should have the default channel.
331 return true;
332 }
333
Julia Reynolds5c399c62019-04-08 14:42:53 -0400334 private void deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400335 PackageManager.NameNotFoundException {
336 if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
337 // Not present
338 return;
339 }
340
341 if (shouldHaveDefaultChannel(r)) {
342 // Keep the default channel until upgraded.
343 return;
344 }
345
346 // Remove Default Channel.
347 r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
348 }
349
Julia Reynolds5c399c62019-04-08 14:42:53 -0400350 private void createDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400351 PackageManager.NameNotFoundException {
352 if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
353 r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
354 com.android.internal.R.string.default_notification_channel_label));
355 return;
356 }
357
358 if (!shouldHaveDefaultChannel(r)) {
359 // Keep the default channel until upgraded.
360 return;
361 }
362
363 // Create Default Channel
364 NotificationChannel channel;
365 channel = new NotificationChannel(
366 NotificationChannel.DEFAULT_CHANNEL_ID,
367 mContext.getString(R.string.default_notification_channel_label),
368 r.importance);
369 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
370 channel.setLockscreenVisibility(r.visibility);
371 if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
372 channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
373 }
374 if (r.priority != DEFAULT_PRIORITY) {
375 channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
376 }
377 if (r.visibility != DEFAULT_VISIBILITY) {
378 channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
379 }
380 r.channels.put(channel.getId(), channel);
381 }
382
Annie Meng8b646fd2019-02-01 18:46:42 +0000383 public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400384 out.startTag(null, TAG_RANKING);
385 out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
Annie Meng8b646fd2019-02-01 18:46:42 +0000386 if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS
387 && (!forBackup || userId == UserHandle.USER_SYSTEM)) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500388 out.startTag(null, TAG_STATUS_ICONS);
389 out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons));
390 out.endTag(null, TAG_STATUS_ICONS);
391 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400392
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400393 synchronized (mPackagePreferences) {
394 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400395 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400396 final PackagePreferences r = mPackagePreferences.valueAt(i);
Annie Meng8b646fd2019-02-01 18:46:42 +0000397 if (forBackup && UserHandle.getUserId(r.uid) != userId) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400398 continue;
399 }
400 final boolean hasNonDefaultSettings =
401 r.importance != DEFAULT_IMPORTANCE
402 || r.priority != DEFAULT_PRIORITY
403 || r.visibility != DEFAULT_VISIBILITY
404 || r.showBadge != DEFAULT_SHOW_BADGE
405 || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
406 || r.channels.size() > 0
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400407 || r.groups.size() > 0
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500408 || r.delegate != null
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800409 || r.allowBubble != DEFAULT_ALLOW_BUBBLE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400410 if (hasNonDefaultSettings) {
411 out.startTag(null, TAG_PACKAGE);
412 out.attribute(null, ATT_NAME, r.pkg);
413 if (r.importance != DEFAULT_IMPORTANCE) {
414 out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
415 }
416 if (r.priority != DEFAULT_PRIORITY) {
417 out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
418 }
419 if (r.visibility != DEFAULT_VISIBILITY) {
420 out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
421 }
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800422 if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) {
423 out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble));
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500424 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400425 out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
426 out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
427 Integer.toString(r.lockedAppFields));
428
429 if (!forBackup) {
430 out.attribute(null, ATT_UID, Integer.toString(r.uid));
431 }
432
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400433 if (r.delegate != null) {
434 out.startTag(null, TAG_DELEGATE);
435
436 out.attribute(null, ATT_NAME, r.delegate.mPkg);
437 out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
438 if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
439 out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
440 }
441 if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
442 out.attribute(null, ATT_USER_ALLOWED,
443 Boolean.toString(r.delegate.mUserAllowed));
444 }
445 out.endTag(null, TAG_DELEGATE);
446 }
447
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400448 for (NotificationChannelGroup group : r.groups.values()) {
449 group.writeXml(out);
450 }
451
452 for (NotificationChannel channel : r.channels.values()) {
453 if (forBackup) {
454 if (!channel.isDeleted()) {
455 channel.writeXmlForBackup(out, mContext);
456 }
457 } else {
458 channel.writeXml(out);
459 }
460 }
461
462 out.endTag(null, TAG_PACKAGE);
463 }
464 }
465 }
466 out.endTag(null, TAG_RANKING);
467 }
468
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800469 /**
470 * Sets whether bubbles are allowed.
471 *
472 * @param pkg the package to allow or not allow bubbles for.
473 * @param uid the uid to allow or not allow bubbles for.
474 * @param allowed whether bubbles are allowed.
475 */
476 public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400477 synchronized (mPackagePreferences) {
478 PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid);
479 p.allowBubble = allowed;
480 p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
481 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500482 }
483
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800484 /**
485 * Whether bubbles are allowed.
486 *
487 * @param pkg the package to check if bubbles are allowed for
488 * @param uid the uid to check if bubbles are allowed for.
489 * @return whether bubbles are allowed.
490 */
Julia Reynolds4509ce72019-01-31 13:12:43 -0500491 @Override
Mady Mellor9db685a2019-01-23 13:23:37 -0800492 public boolean areBubblesAllowed(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400493 synchronized (mPackagePreferences) {
494 return getOrCreatePackagePreferencesLocked(pkg, uid).allowBubble;
495 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500496 }
497
498 public int getAppLockedFields(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400499 synchronized (mPackagePreferences) {
500 return getOrCreatePackagePreferencesLocked(pkg, uid).lockedAppFields;
501 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500502 }
503
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400504 /**
505 * Gets importance.
506 */
507 @Override
508 public int getImportance(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400509 synchronized (mPackagePreferences) {
510 return getOrCreatePackagePreferencesLocked(packageName, uid).importance;
511 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400512 }
513
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400514 /**
515 * Returns whether the importance of the corresponding notification is user-locked and shouldn't
516 * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
517 * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
518 */
519 public boolean getIsAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400520 synchronized (mPackagePreferences) {
521 int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields;
522 return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
523 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400524 }
525
526 @Override
527 public boolean canShowBadge(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400528 synchronized (mPackagePreferences) {
529 return getOrCreatePackagePreferencesLocked(packageName, uid).showBadge;
530 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400531 }
532
533 @Override
534 public void setShowBadge(String packageName, int uid, boolean showBadge) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400535 synchronized (mPackagePreferences) {
536 getOrCreatePackagePreferencesLocked(packageName, uid).showBadge = showBadge;
537 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400538 updateConfig();
539 }
540
541 @Override
542 public boolean isGroupBlocked(String packageName, int uid, String groupId) {
543 if (groupId == null) {
544 return false;
545 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400546 synchronized (mPackagePreferences) {
547 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
548 NotificationChannelGroup group = r.groups.get(groupId);
549 if (group == null) {
550 return false;
551 }
552 return group.isBlocked();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400553 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400554 }
555
556 int getPackagePriority(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400557 synchronized (mPackagePreferences) {
558 return getOrCreatePackagePreferencesLocked(pkg, uid).priority;
559 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400560 }
561
562 int getPackageVisibility(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400563 synchronized (mPackagePreferences) {
564 return getOrCreatePackagePreferencesLocked(pkg, uid).visibility;
565 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400566 }
567
568 @Override
569 public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
570 boolean fromTargetApp) {
571 Preconditions.checkNotNull(pkg);
572 Preconditions.checkNotNull(group);
573 Preconditions.checkNotNull(group.getId());
574 Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
Julia Reynolds5c399c62019-04-08 14:42:53 -0400575 synchronized (mPackagePreferences) {
576 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
577 if (r == null) {
578 throw new IllegalArgumentException("Invalid package");
579 }
580 final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
581 if (!group.equals(oldGroup)) {
582 // will log for new entries as well as name/description changes
583 MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
584 }
585 if (oldGroup != null) {
586 group.setChannels(oldGroup.getChannels());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400587
Julia Reynolds5c399c62019-04-08 14:42:53 -0400588 // apps can't update the blocked status or app overlay permission
589 if (fromTargetApp) {
590 group.setBlocked(oldGroup.isBlocked());
591 group.unlockFields(group.getUserLockedFields());
592 group.lockFields(oldGroup.getUserLockedFields());
593 } else {
594 // but the system can
595 if (group.isBlocked() != oldGroup.isBlocked()) {
596 group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
597 updateChannelsBypassingDnd(mContext.getUserId());
598 }
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -0400599 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400600 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400601 r.groups.put(group.getId(), group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400602 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400603 }
604
605 @Override
606 public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
607 boolean fromTargetApp, boolean hasDndAccess) {
608 Preconditions.checkNotNull(pkg);
609 Preconditions.checkNotNull(channel);
610 Preconditions.checkNotNull(channel.getId());
611 Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
Julia Reynolds5c399c62019-04-08 14:42:53 -0400612 synchronized (mPackagePreferences) {
613 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
614 if (r == null) {
615 throw new IllegalArgumentException("Invalid package");
616 }
617 if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
618 throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
619 }
620 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
621 throw new IllegalArgumentException("Reserved id");
622 }
623 NotificationChannel existing = r.channels.get(channel.getId());
624 // Keep most of the existing settings
625 if (existing != null && fromTargetApp) {
626 if (existing.isDeleted()) {
627 existing.setDeleted(false);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400628
Julia Reynolds5c399c62019-04-08 14:42:53 -0400629 // log a resurrected channel as if it's new again
630 MetricsLogger.action(getChannelLog(channel, pkg).setType(
631 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
632 }
633
634 existing.setName(channel.getName().toString());
635 existing.setDescription(channel.getDescription());
636 existing.setBlockableSystem(channel.isBlockableSystem());
637 if (existing.getGroup() == null) {
638 existing.setGroup(channel.getGroup());
639 }
640
641 // Apps are allowed to downgrade channel importance if the user has not changed any
642 // fields on this channel yet.
643 final int previousExistingImportance = existing.getImportance();
644 if (existing.getUserLockedFields() == 0 &&
645 channel.getImportance() < existing.getImportance()) {
646 existing.setImportance(channel.getImportance());
647 }
648
649 // system apps and dnd access apps can bypass dnd if the user hasn't changed any
650 // fields on the channel yet
651 if (existing.getUserLockedFields() == 0 && hasDndAccess) {
652 boolean bypassDnd = channel.canBypassDnd();
653 existing.setBypassDnd(bypassDnd);
654
655 if (bypassDnd != mAreChannelsBypassingDnd
656 || previousExistingImportance != existing.getImportance()) {
657 updateChannelsBypassingDnd(mContext.getUserId());
658 }
659 }
660
661 updateConfig();
662 return;
663 }
664 if (channel.getImportance() < IMPORTANCE_NONE
665 || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
666 throw new IllegalArgumentException("Invalid importance level");
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400667 }
668
Julia Reynolds5c399c62019-04-08 14:42:53 -0400669 // Reset fields that apps aren't allowed to set.
670 if (fromTargetApp && !hasDndAccess) {
671 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400672 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400673 if (fromTargetApp) {
674 channel.setLockscreenVisibility(r.visibility);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400675 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400676 clearLockedFieldsLocked(channel);
677 channel.setImportanceLockedByOEM(r.oemLockedImportance);
678 if (!channel.isImportanceLockedByOEM()) {
679 if (r.futureOemLockedChannels.remove(channel.getId())) {
680 channel.setImportanceLockedByOEM(true);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400681 }
682 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400683 channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
684 if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
685 channel.setLockscreenVisibility(
686 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
Julia Reynolds413ba842019-01-11 10:38:08 -0500687 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400688 if (!r.showBadge) {
689 channel.setShowBadge(false);
690 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400691
Julia Reynolds5c399c62019-04-08 14:42:53 -0400692 r.channels.put(channel.getId(), channel);
693 if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
694 updateChannelsBypassingDnd(mContext.getUserId());
695 }
696 MetricsLogger.action(getChannelLog(channel, pkg).setType(
697 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400698 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400699 }
700
Julia Reynolds5c399c62019-04-08 14:42:53 -0400701 void clearLockedFieldsLocked(NotificationChannel channel) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400702 channel.unlockFields(channel.getUserLockedFields());
703 }
704
705 @Override
706 public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
707 boolean fromUser) {
708 Preconditions.checkNotNull(updatedChannel);
709 Preconditions.checkNotNull(updatedChannel.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400710 synchronized (mPackagePreferences) {
711 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
712 if (r == null) {
713 throw new IllegalArgumentException("Invalid package");
714 }
715 NotificationChannel channel = r.channels.get(updatedChannel.getId());
716 if (channel == null || channel.isDeleted()) {
717 throw new IllegalArgumentException("Channel does not exist");
718 }
719 if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
720 updatedChannel.setLockscreenVisibility(
721 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
722 }
723 if (fromUser) {
724 updatedChannel.lockFields(channel.getUserLockedFields());
725 lockFieldsForUpdateLocked(channel, updatedChannel);
726 } else {
727 updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
728 }
729 // no importance updates are allowed if OEM blocked it
730 updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
731 if (updatedChannel.isImportanceLockedByOEM()) {
732 updatedChannel.setImportance(channel.getImportance());
733 }
734 updatedChannel.setImportanceLockedByCriticalDeviceFunction(
735 r.defaultAppLockedImportance);
736 if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()) {
737 updatedChannel.setImportance(channel.getImportance());
738 }
Julia Reynolds413ba842019-01-11 10:38:08 -0500739
Julia Reynolds5c399c62019-04-08 14:42:53 -0400740 r.channels.put(updatedChannel.getId(), updatedChannel);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400741
Julia Reynolds5c399c62019-04-08 14:42:53 -0400742 if (onlyHasDefaultChannel(pkg, uid)) {
743 // copy settings to app level so they are inherited by new channels
744 // when the app migrates
745 r.importance = updatedChannel.getImportance();
746 r.priority = updatedChannel.canBypassDnd()
747 ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
748 r.visibility = updatedChannel.getLockscreenVisibility();
749 r.showBadge = updatedChannel.canShowBadge();
750 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400751
Julia Reynolds5c399c62019-04-08 14:42:53 -0400752 if (!channel.equals(updatedChannel)) {
753 // only log if there are real changes
754 MetricsLogger.action(getChannelLog(updatedChannel, pkg)
755 .setSubtype(fromUser ? 1 : 0));
756 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400757
Julia Reynolds5c399c62019-04-08 14:42:53 -0400758 if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
759 || channel.getImportance() != updatedChannel.getImportance()) {
760 updateChannelsBypassingDnd(mContext.getUserId());
761 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400762 }
763 updateConfig();
764 }
765
766 @Override
767 public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
768 boolean includeDeleted) {
769 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400770 synchronized (mPackagePreferences) {
771 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
772 if (r == null) {
773 return null;
774 }
775 if (channelId == null) {
776 channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
777 }
778 final NotificationChannel nc = r.channels.get(channelId);
779 if (nc != null && (includeDeleted || !nc.isDeleted())) {
780 return nc;
781 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400782 return null;
783 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400784 }
785
786 @Override
787 public void deleteNotificationChannel(String pkg, int uid, String channelId) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400788 synchronized (mPackagePreferences) {
789 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
790 if (r == null) {
791 return;
792 }
793 NotificationChannel channel = r.channels.get(channelId);
794 if (channel != null) {
795 channel.setDeleted(true);
796 LogMaker lm = getChannelLog(channel, pkg);
797 lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
798 MetricsLogger.action(lm);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400799
Julia Reynolds5c399c62019-04-08 14:42:53 -0400800 if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
801 updateChannelsBypassingDnd(mContext.getUserId());
802 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400803 }
804 }
805 }
806
807 @Override
808 @VisibleForTesting
809 public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
810 Preconditions.checkNotNull(pkg);
811 Preconditions.checkNotNull(channelId);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400812 synchronized (mPackagePreferences) {
813 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
814 if (r == null) {
815 return;
816 }
817 r.channels.remove(channelId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400818 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400819 }
820
821 @Override
822 public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
823 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400824 synchronized (mPackagePreferences) {
825 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
826 if (r == null) {
827 return;
828 }
829 int N = r.channels.size() - 1;
830 for (int i = N; i >= 0; i--) {
831 String key = r.channels.keyAt(i);
832 if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
833 r.channels.remove(key);
834 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400835 }
836 }
837 }
838
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500839 public boolean shouldHideSilentStatusIcons() {
840 return mHideSilentStatusBarIcons;
841 }
842
843 public void setHideSilentStatusIcons(boolean hide) {
844 mHideSilentStatusBarIcons = hide;
845 }
846
Julia Reynolds413ba842019-01-11 10:38:08 -0500847 public void lockChannelsForOEM(String[] appOrChannelList) {
848 if (appOrChannelList == null) {
849 return;
850 }
851 for (String appOrChannel : appOrChannelList) {
852 if (!TextUtils.isEmpty(appOrChannel)) {
853 String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
854 if (appSplit != null && appSplit.length > 0) {
855 String appName = appSplit[0];
856 String channelId = appSplit.length == 2 ? appSplit[1] : null;
857
858 synchronized (mPackagePreferences) {
859 for (PackagePreferences r : mPackagePreferences.values()) {
860 if (r.pkg.equals(appName)) {
861 if (channelId == null) {
862 // lock all channels for the app
863 r.oemLockedImportance = true;
864 for (NotificationChannel channel : r.channels.values()) {
865 channel.setImportanceLockedByOEM(true);
866 }
867 } else {
868 NotificationChannel channel = r.channels.get(channelId);
869 if (channel != null) {
870 channel.setImportanceLockedByOEM(true);
871 } else {
872 // if this channel shows up in the future, make sure it'll
873 // be locked immediately
874 r.futureOemLockedChannels.add(channelId);
875 }
876 }
877 }
878 }
879 }
880 }
881 }
882 }
883 }
884
Julia Reynolds0c245002019-03-27 16:10:11 -0400885 public void updateDefaultApps(int userId, ArraySet<String> toRemove, ArraySet<String> toAdd) {
886 synchronized (mPackagePreferences) {
887 for (PackagePreferences p : mPackagePreferences.values()) {
888 if (userId == UserHandle.getUserId(p.uid)) {
889 if (toRemove != null && toRemove.contains(p.pkg)) {
890 p.defaultAppLockedImportance = false;
891 for (NotificationChannel channel : p.channels.values()) {
892 channel.setImportanceLockedByCriticalDeviceFunction(false);
893 }
894 } else if (toAdd != null && toAdd.contains(p.pkg)) {
895 p.defaultAppLockedImportance = true;
896 for (NotificationChannel channel : p.channels.values()) {
897 channel.setImportanceLockedByCriticalDeviceFunction(true);
898 }
899 }
900 }
901 }
902 }
903 }
904
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400905 public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
906 int uid, String groupId, boolean includeDeleted) {
907 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400908 synchronized (mPackagePreferences) {
909 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
910 if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
911 return null;
912 }
913 NotificationChannelGroup group = r.groups.get(groupId).clone();
914 group.setChannels(new ArrayList<>());
915 int N = r.channels.size();
916 for (int i = 0; i < N; i++) {
917 final NotificationChannel nc = r.channels.valueAt(i);
918 if (includeDeleted || !nc.isDeleted()) {
919 if (groupId.equals(nc.getGroup())) {
920 group.addChannel(nc);
921 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400922 }
923 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400924 return group;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400925 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400926 }
927
928 public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
929 int uid) {
930 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400931 synchronized (mPackagePreferences) {
932 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
933 if (r == null) {
934 return null;
935 }
936 return r.groups.get(groupId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400937 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400938 }
939
940 @Override
941 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
Julia Reynolds13ed28b2018-09-21 15:20:13 -0400942 int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400943 Preconditions.checkNotNull(pkg);
944 Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -0400945 synchronized (mPackagePreferences) {
946 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
947 if (r == null) {
948 return ParceledListSlice.emptyList();
949 }
950 NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
951 int N = r.channels.size();
952 for (int i = 0; i < N; i++) {
953 final NotificationChannel nc = r.channels.valueAt(i);
954 if (includeDeleted || !nc.isDeleted()) {
955 if (nc.getGroup() != null) {
956 if (r.groups.get(nc.getGroup()) != null) {
957 NotificationChannelGroup ncg = groups.get(nc.getGroup());
958 if (ncg == null) {
959 ncg = r.groups.get(nc.getGroup()).clone();
960 ncg.setChannels(new ArrayList<>());
961 groups.put(nc.getGroup(), ncg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400962
Julia Reynolds5c399c62019-04-08 14:42:53 -0400963 }
964 ncg.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400965 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400966 } else {
967 nonGrouped.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400968 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400969 }
970 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400971 if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
972 groups.put(null, nonGrouped);
973 }
974 if (includeEmpty) {
975 for (NotificationChannelGroup group : r.groups.values()) {
976 if (!groups.containsKey(group.getId())) {
977 groups.put(group.getId(), group);
978 }
Julia Reynolds13ed28b2018-09-21 15:20:13 -0400979 }
980 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400981 return new ParceledListSlice<>(new ArrayList<>(groups.values()));
Julia Reynolds13ed28b2018-09-21 15:20:13 -0400982 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400983 }
984
985 public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
986 String groupId) {
987 List<NotificationChannel> deletedChannels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -0400988 synchronized (mPackagePreferences) {
989 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
990 if (r == null || TextUtils.isEmpty(groupId)) {
991 return deletedChannels;
992 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400993
Julia Reynolds5c399c62019-04-08 14:42:53 -0400994 r.groups.remove(groupId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400995
Julia Reynolds5c399c62019-04-08 14:42:53 -0400996 int N = r.channels.size();
997 for (int i = 0; i < N; i++) {
998 final NotificationChannel nc = r.channels.valueAt(i);
999 if (groupId.equals(nc.getGroup())) {
1000 nc.setDeleted(true);
1001 deletedChannels.add(nc);
1002 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001003 }
1004 }
1005 return deletedChannels;
1006 }
1007
1008 @Override
1009 public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
1010 int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001011 List<NotificationChannelGroup> groups = new ArrayList<>();
1012 synchronized (mPackagePreferences) {
1013 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1014 if (r == null) {
1015 return groups;
1016 }
1017 groups.addAll(r.groups.values());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001018 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001019 return groups;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001020 }
1021
1022 @Override
1023 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
1024 boolean includeDeleted) {
1025 Preconditions.checkNotNull(pkg);
1026 List<NotificationChannel> channels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001027 synchronized (mPackagePreferences) {
1028 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1029 if (r == null) {
1030 return ParceledListSlice.emptyList();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001031 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001032 int N = r.channels.size();
1033 for (int i = 0; i < N; i++) {
1034 final NotificationChannel nc = r.channels.valueAt(i);
1035 if (includeDeleted || !nc.isDeleted()) {
1036 channels.add(nc);
1037 }
1038 }
1039 return new ParceledListSlice<>(channels);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001040 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001041 }
1042
1043 /**
Beverly0479cde22018-11-09 11:05:34 -05001044 * Gets all notification channels associated with the given pkg and userId that can bypass dnd
1045 */
1046 public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
1047 int userId) {
1048 List<NotificationChannel> channels = new ArrayList<>();
1049 synchronized (mPackagePreferences) {
1050 final PackagePreferences r = mPackagePreferences.get(
1051 packagePreferencesKey(pkg, userId));
1052 // notifications from this package aren't blocked
1053 if (r != null && r.importance != IMPORTANCE_NONE) {
1054 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001055 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001056 channels.add(channel);
1057 }
1058 }
1059 }
1060 }
1061 return new ParceledListSlice<>(channels);
1062 }
1063
1064 /**
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001065 * True for pre-O apps that only have the default channel, or pre O apps that have no
1066 * channels yet. This method will create the default channel for pre-O apps that don't have it.
1067 * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
1068 * upgrades.
1069 */
1070 public boolean onlyHasDefaultChannel(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001071 synchronized (mPackagePreferences) {
1072 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1073 if (r.channels.size() == 1
1074 && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
1075 return true;
1076 }
1077 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001078 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001079 }
1080
1081 public int getDeletedChannelCount(String pkg, int uid) {
1082 Preconditions.checkNotNull(pkg);
1083 int deletedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001084 synchronized (mPackagePreferences) {
1085 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1086 if (r == null) {
1087 return deletedCount;
1088 }
1089 int N = r.channels.size();
1090 for (int i = 0; i < N; i++) {
1091 final NotificationChannel nc = r.channels.valueAt(i);
1092 if (nc.isDeleted()) {
1093 deletedCount++;
1094 }
1095 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001096 return deletedCount;
1097 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001098 }
1099
1100 public int getBlockedChannelCount(String pkg, int uid) {
1101 Preconditions.checkNotNull(pkg);
1102 int blockedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001103 synchronized (mPackagePreferences) {
1104 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1105 if (r == null) {
1106 return blockedCount;
1107 }
1108 int N = r.channels.size();
1109 for (int i = 0; i < N; i++) {
1110 final NotificationChannel nc = r.channels.valueAt(i);
1111 if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
1112 blockedCount++;
1113 }
1114 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001115 return blockedCount;
1116 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001117 }
1118
1119 public int getBlockedAppCount(int userId) {
1120 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001121 synchronized (mPackagePreferences) {
1122 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001123 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001124 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001125 if (userId == UserHandle.getUserId(r.uid)
1126 && r.importance == IMPORTANCE_NONE) {
1127 count++;
1128 }
1129 }
1130 }
1131 return count;
1132 }
1133
Beverly0479cde22018-11-09 11:05:34 -05001134 /**
1135 * Returns the number of apps that have at least one notification channel that can bypass DND
1136 * for given particular user
1137 */
1138 public int getAppsBypassingDndCount(int userId) {
1139 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001140 synchronized (mPackagePreferences) {
Beverly0479cde22018-11-09 11:05:34 -05001141 final int numPackagePreferences = mPackagePreferences.size();
1142 for (int i = 0; i < numPackagePreferences; i++) {
1143 final PackagePreferences r = mPackagePreferences.valueAt(i);
1144 // Package isn't associated with this userId or notifications from this package are
1145 // blocked
1146 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1147 continue;
1148 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001149
Beverly0479cde22018-11-09 11:05:34 -05001150 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001151 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001152 count++;
1153 break;
1154 }
1155 }
1156 }
1157 }
1158 return count;
1159 }
1160
1161 /**
1162 * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
1163 * updating
1164 * @param userId
1165 */
1166 private void syncChannelsBypassingDnd(int userId) {
1167 mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
1168 & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
1169 updateChannelsBypassingDnd(userId);
1170 }
1171
1172 /**
1173 * Updates the user's NotificationPolicy based on whether the given userId
1174 * has channels bypassing DND
1175 * @param userId
1176 */
1177 private void updateChannelsBypassingDnd(int userId) {
1178 synchronized (mPackagePreferences) {
1179 final int numPackagePreferences = mPackagePreferences.size();
1180 for (int i = 0; i < numPackagePreferences; i++) {
1181 final PackagePreferences r = mPackagePreferences.valueAt(i);
1182 // Package isn't associated with this userId or notifications from this package are
1183 // blocked
1184 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1185 continue;
1186 }
1187
1188 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001189 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001190 if (!mAreChannelsBypassingDnd) {
1191 mAreChannelsBypassingDnd = true;
1192 updateZenPolicy(true);
1193 }
1194 return;
1195 }
1196 }
1197 }
1198 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001199 // If no channels bypass DND, update the zen policy once to disable DND bypass.
1200 if (mAreChannelsBypassingDnd) {
1201 mAreChannelsBypassingDnd = false;
1202 updateZenPolicy(false);
1203 }
1204 }
1205
Julia Reynolds5c399c62019-04-08 14:42:53 -04001206 private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
Beverly0479cde22018-11-09 11:05:34 -05001207 // Channel is in a group that's blocked
Beverly4f7b53d2018-11-20 09:56:31 -05001208 if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
1209 return false;
Beverly0479cde22018-11-09 11:05:34 -05001210 }
1211
1212 // Channel is deleted or is blocked
1213 if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
1214 return false;
1215 }
1216
1217 return true;
1218 }
1219
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001220 public void updateZenPolicy(boolean areChannelsBypassingDnd) {
1221 NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
1222 mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
1223 policy.priorityCategories, policy.priorityCallSenders,
1224 policy.priorityMessageSenders, policy.suppressedVisualEffects,
1225 (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
1226 : 0)));
1227 }
1228
1229 public boolean areChannelsBypassingDnd() {
1230 return mAreChannelsBypassingDnd;
1231 }
1232
1233 /**
1234 * Sets importance.
1235 */
1236 @Override
1237 public void setImportance(String pkgName, int uid, int importance) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001238 synchronized (mPackagePreferences) {
1239 getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance;
1240 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001241 updateConfig();
1242 }
1243
1244 public void setEnabled(String packageName, int uid, boolean enabled) {
1245 boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
1246 if (wasEnabled == enabled) {
1247 return;
1248 }
1249 setImportance(packageName, uid,
1250 enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
1251 }
1252
1253 /**
1254 * Sets whether any notifications from the app, represented by the given {@code pkgName} and
1255 * {@code uid}, have their importance locked by the user. Locked notifications don't get
1256 * considered for sentiment adjustments (and thus never show a blocking helper).
1257 */
1258 public void setAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001259 synchronized (mPackagePreferences) {
1260 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(packageName, uid);
1261 if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
1262 return;
1263 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001264
Julia Reynolds5c399c62019-04-08 14:42:53 -04001265 prefs.lockedAppFields =
1266 prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
1267 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001268 updateConfig();
1269 }
1270
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001271 /**
1272 * Returns the delegate for a given package, if it's allowed by the package and the user.
1273 */
1274 public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001275 synchronized (mPackagePreferences) {
1276 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001277
Julia Reynolds5c399c62019-04-08 14:42:53 -04001278 if (prefs == null || prefs.delegate == null) {
1279 return null;
1280 }
1281 if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
1282 return null;
1283 }
1284 return prefs.delegate.mPkg;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001285 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001286 }
1287
1288 /**
1289 * Used by an app to delegate notification posting privileges to another apps.
1290 */
1291 public void setNotificationDelegate(String sourcePkg, int sourceUid,
1292 String delegatePkg, int delegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001293 synchronized (mPackagePreferences) {
1294 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001295
Julia Reynolds5c399c62019-04-08 14:42:53 -04001296 boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
1297 Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
1298 prefs.delegate = delegate;
1299 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001300 updateConfig();
1301 }
1302
1303 /**
1304 * Used by an app to turn off its notification delegate.
1305 */
1306 public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001307 boolean changed = false;
1308 synchronized (mPackagePreferences) {
1309 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1310 if (prefs != null && prefs.delegate != null) {
1311 prefs.delegate.mEnabled = false;
1312 changed = true;
1313 }
1314 }
1315 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001316 updateConfig();
1317 }
1318 }
1319
1320 /**
1321 * Toggles whether an app can have a notification delegate on behalf of a user.
1322 */
1323 public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001324 boolean changed = false;
1325 synchronized (mPackagePreferences) {
1326 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1327 if (prefs != null && prefs.delegate != null) {
1328 prefs.delegate.mUserAllowed = userAllowed;
1329 changed = true;
1330 }
1331 }
1332 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001333 updateConfig();
1334 }
1335 }
1336
1337 /**
1338 * Returns whether the given app is allowed on post notifications on behalf of the other given
1339 * app.
1340 */
1341 public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
1342 String potentialDelegatePkg, int potentialDelegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001343 synchronized (mPackagePreferences) {
1344 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001345
Julia Reynolds5c399c62019-04-08 14:42:53 -04001346 return prefs != null && prefs.isValidDelegate(potentialDelegatePkg,
1347 potentialDelegateUid);
1348 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001349 }
1350
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001351 @VisibleForTesting
Julia Reynolds5c399c62019-04-08 14:42:53 -04001352 void lockFieldsForUpdateLocked(NotificationChannel original, NotificationChannel update) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001353 if (original.canBypassDnd() != update.canBypassDnd()) {
1354 update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
1355 }
1356 if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
1357 update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
1358 }
1359 if (original.getImportance() != update.getImportance()) {
1360 update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
1361 }
1362 if (original.shouldShowLights() != update.shouldShowLights()
1363 || original.getLightColor() != update.getLightColor()) {
1364 update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
1365 }
1366 if (!Objects.equals(original.getSound(), update.getSound())) {
1367 update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
1368 }
1369 if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
1370 || original.shouldVibrate() != update.shouldVibrate()) {
1371 update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
1372 }
1373 if (original.canShowBadge() != update.canShowBadge()) {
1374 update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
1375 }
Julia Reynolds4509ce72019-01-31 13:12:43 -05001376 if (original.canBubble() != update.canBubble()) {
Mady Mellorc39b4ae2019-01-09 17:11:37 -08001377 update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE);
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -04001378 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001379 }
1380
1381 public void dump(PrintWriter pw, String prefix,
1382 @NonNull NotificationManagerService.DumpFilter filter) {
1383 pw.print(prefix);
1384 pw.println("per-package config:");
1385
Julia Reynolds5c399c62019-04-08 14:42:53 -04001386 pw.println("PackagePreferences:");
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001387 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001388 dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001389 }
1390 pw.println("Restored without uid:");
Julia Reynolds5c399c62019-04-08 14:42:53 -04001391 dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001392 }
1393
1394 public void dump(ProtoOutputStream proto,
1395 @NonNull NotificationManagerService.DumpFilter filter) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001396 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001397 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001398 mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001399 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001400 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001401 mRestoredWithoutUids);
1402 }
1403
Julia Reynolds5c399c62019-04-08 14:42:53 -04001404 private static void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001405 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001406 ArrayMap<String, PackagePreferences> packagePreferences) {
1407 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001408 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001409 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001410 if (filter.matches(r.pkg)) {
1411 pw.print(prefix);
1412 pw.print(" AppSettings: ");
1413 pw.print(r.pkg);
1414 pw.print(" (");
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001415 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001416 pw.print(')');
1417 if (r.importance != DEFAULT_IMPORTANCE) {
1418 pw.print(" importance=");
1419 pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
1420 }
1421 if (r.priority != DEFAULT_PRIORITY) {
1422 pw.print(" priority=");
1423 pw.print(Notification.priorityToString(r.priority));
1424 }
1425 if (r.visibility != DEFAULT_VISIBILITY) {
1426 pw.print(" visibility=");
1427 pw.print(Notification.visibilityToString(r.visibility));
1428 }
1429 pw.print(" showBadge=");
1430 pw.print(Boolean.toString(r.showBadge));
1431 pw.println();
1432 for (NotificationChannel channel : r.channels.values()) {
1433 pw.print(prefix);
1434 channel.dump(pw, " ", filter.redact);
1435 }
1436 for (NotificationChannelGroup group : r.groups.values()) {
1437 pw.print(prefix);
1438 pw.print(" ");
1439 pw.print(" ");
1440 pw.println(group);
1441 }
1442 }
1443 }
1444 }
1445
Julia Reynolds5c399c62019-04-08 14:42:53 -04001446 private static void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001447 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001448 ArrayMap<String, PackagePreferences> packagePreferences) {
1449 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001450 long fToken;
1451 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001452 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001453 if (filter.matches(r.pkg)) {
1454 fToken = proto.start(fieldId);
1455
1456 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
1457 proto.write(RankingHelperProto.RecordProto.UID, r.uid);
1458 proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
1459 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
1460 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
1461 proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
1462
1463 for (NotificationChannel channel : r.channels.values()) {
1464 channel.writeToProto(proto, RankingHelperProto.RecordProto.CHANNELS);
1465 }
1466 for (NotificationChannelGroup group : r.groups.values()) {
1467 group.writeToProto(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS);
1468 }
1469
1470 proto.end(fToken);
1471 }
1472 }
1473 }
1474
1475 public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
1476 JSONObject ranking = new JSONObject();
1477 JSONArray PackagePreferencess = new JSONArray();
1478 try {
1479 ranking.put("noUid", mRestoredWithoutUids.size());
1480 } catch (JSONException e) {
1481 // pass
1482 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001483 synchronized (mPackagePreferences) {
1484 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001485 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001486 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001487 if (filter == null || filter.matches(r.pkg)) {
1488 JSONObject PackagePreferences = new JSONObject();
1489 try {
1490 PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
1491 PackagePreferences.put("packageName", r.pkg);
1492 if (r.importance != DEFAULT_IMPORTANCE) {
1493 PackagePreferences.put("importance",
1494 NotificationListenerService.Ranking.importanceToString(
1495 r.importance));
1496 }
1497 if (r.priority != DEFAULT_PRIORITY) {
1498 PackagePreferences.put("priority",
1499 Notification.priorityToString(r.priority));
1500 }
1501 if (r.visibility != DEFAULT_VISIBILITY) {
1502 PackagePreferences.put("visibility",
1503 Notification.visibilityToString(r.visibility));
1504 }
1505 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1506 PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
1507 }
1508 JSONArray channels = new JSONArray();
1509 for (NotificationChannel channel : r.channels.values()) {
1510 channels.put(channel.toJson());
1511 }
1512 PackagePreferences.put("channels", channels);
1513 JSONArray groups = new JSONArray();
1514 for (NotificationChannelGroup group : r.groups.values()) {
1515 groups.put(group.toJson());
1516 }
1517 PackagePreferences.put("groups", groups);
1518 } catch (JSONException e) {
1519 // pass
1520 }
1521 PackagePreferencess.put(PackagePreferences);
1522 }
1523 }
1524 }
1525 try {
1526 ranking.put("PackagePreferencess", PackagePreferencess);
1527 } catch (JSONException e) {
1528 // pass
1529 }
1530 return ranking;
1531 }
1532
1533 /**
1534 * Dump only the ban information as structured JSON for the stats collector.
1535 *
1536 * This is intentionally redundant with {#link dumpJson} because the old
1537 * scraper will expect this format.
1538 *
1539 * @param filter
1540 * @return
1541 */
1542 public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
1543 JSONArray bans = new JSONArray();
1544 Map<Integer, String> packageBans = getPackageBans();
1545 for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
1546 final int userId = UserHandle.getUserId(ban.getKey());
1547 final String packageName = ban.getValue();
1548 if (filter == null || filter.matches(packageName)) {
1549 JSONObject banJson = new JSONObject();
1550 try {
1551 banJson.put("userId", userId);
1552 banJson.put("packageName", packageName);
1553 } catch (JSONException e) {
1554 e.printStackTrace();
1555 }
1556 bans.put(banJson);
1557 }
1558 }
1559 return bans;
1560 }
1561
1562 public Map<Integer, String> getPackageBans() {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001563 synchronized (mPackagePreferences) {
1564 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001565 ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
1566 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001567 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001568 if (r.importance == IMPORTANCE_NONE) {
1569 packageBans.put(r.uid, r.pkg);
1570 }
1571 }
1572
1573 return packageBans;
1574 }
1575 }
1576
1577 /**
1578 * Dump only the channel information as structured JSON for the stats collector.
1579 *
1580 * This is intentionally redundant with {#link dumpJson} because the old
1581 * scraper will expect this format.
1582 *
1583 * @param filter
1584 * @return
1585 */
1586 public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
1587 JSONArray channels = new JSONArray();
1588 Map<String, Integer> packageChannels = getPackageChannels();
1589 for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) {
1590 final String packageName = channelCount.getKey();
1591 if (filter == null || filter.matches(packageName)) {
1592 JSONObject channelCountJson = new JSONObject();
1593 try {
1594 channelCountJson.put("packageName", packageName);
1595 channelCountJson.put("channelCount", channelCount.getValue());
1596 } catch (JSONException e) {
1597 e.printStackTrace();
1598 }
1599 channels.put(channelCountJson);
1600 }
1601 }
1602 return channels;
1603 }
1604
1605 private Map<String, Integer> getPackageChannels() {
1606 ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001607 synchronized (mPackagePreferences) {
1608 for (int i = 0; i < mPackagePreferences.size(); i++) {
1609 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001610 int channelCount = 0;
1611 for (int j = 0; j < r.channels.size(); j++) {
1612 if (!r.channels.valueAt(j).isDeleted()) {
1613 channelCount++;
1614 }
1615 }
1616 packageChannels.put(r.pkg, channelCount);
1617 }
1618 }
1619 return packageChannels;
1620 }
1621
Beverly0479cde22018-11-09 11:05:34 -05001622 /**
1623 * Called when user switches
1624 */
1625 public void onUserSwitched(int userId) {
1626 syncChannelsBypassingDnd(userId);
1627 }
1628
1629 /**
1630 * Called when user is unlocked
1631 */
1632 public void onUserUnlocked(int userId) {
1633 syncChannelsBypassingDnd(userId);
1634 }
1635
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001636 public void onUserRemoved(int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001637 synchronized (mPackagePreferences) {
1638 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001639 for (int i = N - 1; i >= 0; i--) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001640 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001641 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001642 mPackagePreferences.removeAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001643 }
1644 }
1645 }
1646 }
1647
1648 protected void onLocaleChanged(Context context, int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001649 synchronized (mPackagePreferences) {
1650 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001651 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001652 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001653 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
1654 if (PackagePreferences.channels.containsKey(
1655 NotificationChannel.DEFAULT_CHANNEL_ID)) {
1656 PackagePreferences.channels.get(
1657 NotificationChannel.DEFAULT_CHANNEL_ID).setName(
1658 context.getResources().getString(
1659 R.string.default_notification_channel_label));
1660 }
1661 }
1662 }
1663 }
1664 }
1665
1666 public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
1667 int[] uidList) {
1668 if (pkgList == null || pkgList.length == 0) {
1669 return; // nothing to do
1670 }
1671 boolean updated = false;
1672 if (removingPackage) {
1673 // Remove notification settings for uninstalled package
1674 int size = Math.min(pkgList.length, uidList.length);
1675 for (int i = 0; i < size; i++) {
1676 final String pkg = pkgList[i];
1677 final int uid = uidList[i];
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001678 synchronized (mPackagePreferences) {
1679 mPackagePreferences.remove(packagePreferencesKey(pkg, uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001680 }
1681 mRestoredWithoutUids.remove(pkg);
1682 updated = true;
1683 }
1684 } else {
1685 for (String pkg : pkgList) {
1686 // Package install
1687 final PackagePreferences r = mRestoredWithoutUids.get(pkg);
1688 if (r != null) {
1689 try {
1690 r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
1691 mRestoredWithoutUids.remove(pkg);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001692 synchronized (mPackagePreferences) {
1693 mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001694 }
1695 updated = true;
1696 } catch (PackageManager.NameNotFoundException e) {
1697 // noop
1698 }
1699 }
1700 // Package upgrade
1701 try {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001702 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001703 PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001704 mPm.getPackageUidAsUser(pkg, changeUserId));
1705 if (fullPackagePreferences != null) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001706 createDefaultChannelIfNeededLocked(fullPackagePreferences);
1707 deleteDefaultChannelIfNeededLocked(fullPackagePreferences);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001708 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001709 }
1710 } catch (PackageManager.NameNotFoundException e) {
1711 }
1712 }
1713 }
1714
1715 if (updated) {
1716 updateConfig();
1717 }
1718 }
1719
Julia Reynolds7af51c52019-04-19 11:08:27 -04001720 public void clearData(String pkg, int uid) {
1721 synchronized (mPackagePreferences) {
1722 PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
1723 if (p != null) {
1724 p.channels = new ArrayMap<>();
1725 p.groups = new ArrayMap<>();
1726 p.delegate = null;
1727 p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
1728 p.allowBubble = DEFAULT_ALLOW_BUBBLE;
1729 p.importance = DEFAULT_IMPORTANCE;
1730 p.priority = DEFAULT_PRIORITY;
1731 p.visibility = DEFAULT_VISIBILITY;
1732 p.showBadge = DEFAULT_SHOW_BADGE;
1733 }
1734 }
1735 }
1736
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001737 private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
1738 return new LogMaker(
1739 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1740 .ACTION_NOTIFICATION_CHANNEL)
1741 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1742 .setPackageName(pkg)
1743 .addTaggedData(
1744 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1745 .FIELD_NOTIFICATION_CHANNEL_ID,
1746 channel.getId())
1747 .addTaggedData(
1748 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1749 .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
1750 channel.getImportance());
1751 }
1752
1753 private LogMaker getChannelGroupLog(String groupId, String pkg) {
1754 return new LogMaker(
1755 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1756 .ACTION_NOTIFICATION_CHANNEL_GROUP)
1757 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1758 .addTaggedData(
1759 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1760 .FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
1761 groupId)
1762 .setPackageName(pkg);
1763 }
1764
Julia Reynolds4509ce72019-01-31 13:12:43 -05001765 public void updateBubblesEnabled() {
1766 if (mBubblesEnabled == null) {
1767 mBubblesEnabled = new SparseBooleanArray();
1768 }
1769 boolean changed = false;
1770 // update the cached values
1771 for (int index = 0; index < mBubblesEnabled.size(); index++) {
1772 int userId = mBubblesEnabled.keyAt(index);
1773 final boolean oldValue = mBubblesEnabled.get(userId);
1774 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1775 Settings.Secure.NOTIFICATION_BUBBLES,
1776 DEFAULT_ALLOW_BUBBLE ? 1 : 0, userId) != 0;
1777 mBubblesEnabled.put(userId, newValue);
1778 changed |= oldValue != newValue;
1779 }
1780 if (changed) {
1781 updateConfig();
1782 }
1783 }
1784
1785 public boolean bubblesEnabled(UserHandle userHandle) {
1786 int userId = userHandle.getIdentifier();
1787 if (userId == UserHandle.USER_ALL) {
1788 return false;
1789 }
1790 if (mBubblesEnabled.indexOfKey(userId) < 0) {
1791 mBubblesEnabled.put(userId,
1792 Settings.Secure.getIntForUser(mContext.getContentResolver(),
1793 Settings.Secure.NOTIFICATION_BUBBLES,
1794 DEFAULT_ALLOW_BUBBLE ? 1 : 0, userId) != 0);
1795 }
1796 return mBubblesEnabled.get(userId, DEFAULT_ALLOW_BUBBLE);
1797 }
1798
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001799
1800 public void updateBadgingEnabled() {
1801 if (mBadgingEnabled == null) {
1802 mBadgingEnabled = new SparseBooleanArray();
1803 }
1804 boolean changed = false;
1805 // update the cached values
1806 for (int index = 0; index < mBadgingEnabled.size(); index++) {
1807 int userId = mBadgingEnabled.keyAt(index);
1808 final boolean oldValue = mBadgingEnabled.get(userId);
1809 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1810 Settings.Secure.NOTIFICATION_BADGING,
1811 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
1812 mBadgingEnabled.put(userId, newValue);
1813 changed |= oldValue != newValue;
1814 }
1815 if (changed) {
1816 updateConfig();
1817 }
1818 }
1819
1820 public boolean badgingEnabled(UserHandle userHandle) {
1821 int userId = userHandle.getIdentifier();
1822 if (userId == UserHandle.USER_ALL) {
1823 return false;
1824 }
1825 if (mBadgingEnabled.indexOfKey(userId) < 0) {
1826 mBadgingEnabled.put(userId,
1827 Settings.Secure.getIntForUser(mContext.getContentResolver(),
1828 Settings.Secure.NOTIFICATION_BADGING,
1829 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
1830 }
1831 return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
1832 }
1833
1834 private void updateConfig() {
1835 mRankingHandler.requestSort();
1836 }
1837
1838 private static String packagePreferencesKey(String pkg, int uid) {
1839 return pkg + "|" + uid;
1840 }
1841
1842 private static class PackagePreferences {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001843 String pkg;
1844 int uid = UNKNOWN_UID;
1845 int importance = DEFAULT_IMPORTANCE;
1846 int priority = DEFAULT_PRIORITY;
1847 int visibility = DEFAULT_VISIBILITY;
1848 boolean showBadge = DEFAULT_SHOW_BADGE;
Mady Mellorc39b4ae2019-01-09 17:11:37 -08001849 boolean allowBubble = DEFAULT_ALLOW_BUBBLE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001850 int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Julia Reynolds0c245002019-03-27 16:10:11 -04001851 // these fields are loaded on boot from a different source of truth and so are not
1852 // written to notification policy xml
Julia Reynolds413ba842019-01-11 10:38:08 -05001853 boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
1854 List<String> futureOemLockedChannels = new ArrayList<>();
Julia Reynolds0c245002019-03-27 16:10:11 -04001855 boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001856
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001857 Delegate delegate = null;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001858 ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
1859 Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001860
1861 public boolean isValidDelegate(String pkg, int uid) {
1862 return delegate != null && delegate.isAllowed(pkg, uid);
1863 }
1864 }
1865
1866 private static class Delegate {
1867 static final boolean DEFAULT_ENABLED = true;
1868 static final boolean DEFAULT_USER_ALLOWED = true;
1869 String mPkg;
1870 int mUid = UNKNOWN_UID;
1871 boolean mEnabled = DEFAULT_ENABLED;
1872 boolean mUserAllowed = DEFAULT_USER_ALLOWED;
1873
1874 Delegate(String pkg, int uid, boolean enabled, boolean userAllowed) {
1875 mPkg = pkg;
1876 mUid = uid;
1877 mEnabled = enabled;
1878 mUserAllowed = userAllowed;
1879 }
1880
1881 public boolean isAllowed(String pkg, int uid) {
1882 if (pkg == null || uid == UNKNOWN_UID) {
1883 return false;
1884 }
1885 return pkg.equals(mPkg)
1886 && uid == mUid
1887 && (mUserAllowed && mEnabled);
1888 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001889 }
1890}