blob: 0804a738424a61d21b75ce0f2ce1f861b384f441 [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;
Julia Reynoldse7ca31b2019-04-25 15:41:47 -040041import android.util.Pair;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040042import android.util.Slog;
43import android.util.SparseBooleanArray;
44import android.util.proto.ProtoOutputStream;
45
46import com.android.internal.R;
47import com.android.internal.annotations.VisibleForTesting;
48import com.android.internal.logging.MetricsLogger;
49import com.android.internal.util.Preconditions;
50import com.android.internal.util.XmlUtils;
51
52import org.json.JSONArray;
53import org.json.JSONException;
54import org.json.JSONObject;
55import org.xmlpull.v1.XmlPullParser;
56import org.xmlpull.v1.XmlPullParserException;
57import org.xmlpull.v1.XmlSerializer;
58
59import java.io.IOException;
60import java.io.PrintWriter;
61import java.util.ArrayList;
62import java.util.Arrays;
63import java.util.Collection;
64import java.util.List;
65import java.util.Map;
66import java.util.Objects;
67import java.util.concurrent.ConcurrentHashMap;
68
69public class PreferencesHelper implements RankingConfig {
70 private static final String TAG = "NotificationPrefHelper";
71 private static final int XML_VERSION = 1;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040072 private static final int UNKNOWN_UID = UserHandle.USER_NULL;
Julia Reynolds413ba842019-01-11 10:38:08 -050073 private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040074
75 @VisibleForTesting
76 static final String TAG_RANKING = "ranking";
77 private static final String TAG_PACKAGE = "package";
78 private static final String TAG_CHANNEL = "channel";
79 private static final String TAG_GROUP = "channelGroup";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040080 private static final String TAG_DELEGATE = "delegate";
Julia Reynolds12ad7ca2019-01-28 09:29:16 -050081 private static final String TAG_STATUS_ICONS = "status_icons";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040082
83 private static final String ATT_VERSION = "version";
84 private static final String ATT_NAME = "name";
85 private static final String ATT_UID = "uid";
86 private static final String ATT_ID = "id";
Mady Mellorc39b4ae2019-01-09 17:11:37 -080087 private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040088 private static final String ATT_PRIORITY = "priority";
89 private static final String ATT_VISIBILITY = "visibility";
90 private static final String ATT_IMPORTANCE = "importance";
91 private static final String ATT_SHOW_BADGE = "show_badge";
92 private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040093 private static final String ATT_ENABLED = "enabled";
94 private static final String ATT_USER_ALLOWED = "allowed";
Julia Reynolds25692c42019-05-03 15:01:24 -040095 private static final String ATT_HIDE_SILENT = "hide_gentle";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040096
97 private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
98 private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
99 private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500100 @VisibleForTesting
Julia Reynolds25692c42019-05-03 15:01:24 -0400101 static final boolean DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS = false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400102 private static final boolean DEFAULT_SHOW_BADGE = true;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800103 private static final boolean DEFAULT_ALLOW_BUBBLE = true;
Julia Reynolds413ba842019-01-11 10:38:08 -0500104 private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false;
Julia Reynolds0c245002019-03-27 16:10:11 -0400105 private static final boolean DEFAULT_APP_LOCKED_IMPORTANCE = false;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800106
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400107 /**
108 * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
109 * fields.
110 */
111 private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
112
113 /**
114 * All user-lockable fields for a given application.
115 */
116 @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
117 public @interface LockableAppFields {
118 int USER_LOCKED_IMPORTANCE = 0x00000001;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800119 int USER_LOCKED_BUBBLE = 0x00000002;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400120 }
121
122 // pkg|uid => PackagePreferences
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400123 private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400124 // pkg => PackagePreferences
125 private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
126
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400127 private final Context mContext;
128 private final PackageManager mPm;
129 private final RankingHandler mRankingHandler;
130 private final ZenModeHelper mZenModeHelper;
131
132 private SparseBooleanArray mBadgingEnabled;
Julia Reynolds4509ce72019-01-31 13:12:43 -0500133 private SparseBooleanArray mBubblesEnabled;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400134 private boolean mAreChannelsBypassingDnd;
Julia Reynolds2594b472019-04-03 13:30:16 -0400135 private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400136
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400137 public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
138 ZenModeHelper zenHelper) {
139 mContext = context;
140 mZenModeHelper = zenHelper;
141 mRankingHandler = rankingHandler;
142 mPm = pm;
143
144 updateBadgingEnabled();
Julia Reynolds4509ce72019-01-31 13:12:43 -0500145 updateBubblesEnabled();
Beverly0479cde22018-11-09 11:05:34 -0500146 syncChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400147 }
148
Annie Meng8b646fd2019-02-01 18:46:42 +0000149 public void readXml(XmlPullParser parser, boolean forRestore, int userId)
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400150 throws XmlPullParserException, IOException {
151 int type = parser.getEventType();
152 if (type != XmlPullParser.START_TAG) return;
153 String tag = parser.getName();
154 if (!TAG_RANKING.equals(tag)) return;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400155 synchronized (mPackagePreferences) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500156 // Clobber groups and channels with the xml, but don't delete other data that wasn't
157 // present at the time of serialization.
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400158 mRestoredWithoutUids.clear();
159 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
160 tag = parser.getName();
161 if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
162 return;
163 }
164 if (type == XmlPullParser.START_TAG) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500165 if (TAG_STATUS_ICONS.equals(tag)) {
Annie Meng8b646fd2019-02-01 18:46:42 +0000166 if (forRestore && userId != UserHandle.USER_SYSTEM) {
167 continue;
168 }
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500169 mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute(
170 parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
171 } else if (TAG_PACKAGE.equals(tag)) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400172 int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
173 String name = parser.getAttributeValue(null, ATT_NAME);
174 if (!TextUtils.isEmpty(name)) {
175 if (forRestore) {
176 try {
Annie Meng8b646fd2019-02-01 18:46:42 +0000177 uid = mPm.getPackageUidAsUser(name, userId);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400178 } catch (PackageManager.NameNotFoundException e) {
179 // noop
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400180 }
181 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400182
Julia Reynolds5c399c62019-04-08 14:42:53 -0400183 PackagePreferences r = getOrCreatePackagePreferencesLocked(name, uid,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400184 XmlUtils.readIntAttribute(
185 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
186 XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
187 DEFAULT_PRIORITY),
188 XmlUtils.readIntAttribute(
189 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
190 XmlUtils.readBooleanAttribute(
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500191 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
192 XmlUtils.readBooleanAttribute(
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800193 parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400194 r.importance = XmlUtils.readIntAttribute(
195 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
196 r.priority = XmlUtils.readIntAttribute(
197 parser, ATT_PRIORITY, DEFAULT_PRIORITY);
198 r.visibility = XmlUtils.readIntAttribute(
199 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
200 r.showBadge = XmlUtils.readBooleanAttribute(
201 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
202 r.lockedAppFields = XmlUtils.readIntAttribute(parser,
203 ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
204
205 final int innerDepth = parser.getDepth();
206 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
207 && (type != XmlPullParser.END_TAG
208 || parser.getDepth() > innerDepth)) {
209 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
210 continue;
211 }
212
213 String tagName = parser.getName();
214 // Channel groups
215 if (TAG_GROUP.equals(tagName)) {
216 String id = parser.getAttributeValue(null, ATT_ID);
217 CharSequence groupName = parser.getAttributeValue(null,
218 ATT_NAME);
219 if (!TextUtils.isEmpty(id)) {
220 NotificationChannelGroup group
221 = new NotificationChannelGroup(id, groupName);
222 group.populateFromXml(parser);
223 r.groups.put(id, group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400224 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400225 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400226 // Channels
227 if (TAG_CHANNEL.equals(tagName)) {
228 String id = parser.getAttributeValue(null, ATT_ID);
229 String channelName = parser.getAttributeValue(null, ATT_NAME);
230 int channelImportance = XmlUtils.readIntAttribute(
231 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
232 if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
233 NotificationChannel channel = new NotificationChannel(id,
234 channelName, channelImportance);
235 if (forRestore) {
236 channel.populateFromXmlForRestore(parser, mContext);
237 } else {
238 channel.populateFromXml(parser);
239 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -0400240 channel.setImportanceLockedByCriticalDeviceFunction(
241 r.defaultAppLockedImportance);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400242 r.channels.put(id, channel);
243 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400244 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400245 // Delegate
246 if (TAG_DELEGATE.equals(tagName)) {
247 int delegateId =
248 XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
249 String delegateName =
250 XmlUtils.readStringAttribute(parser, ATT_NAME);
251 boolean delegateEnabled = XmlUtils.readBooleanAttribute(
252 parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
253 boolean userAllowed = XmlUtils.readBooleanAttribute(
254 parser, ATT_USER_ALLOWED,
255 Delegate.DEFAULT_USER_ALLOWED);
256 Delegate d = null;
257 if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(
258 delegateName)) {
259 d = new Delegate(
260 delegateName, delegateId, delegateEnabled,
261 userAllowed);
262 }
263 r.delegate = d;
264 }
265
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400266 }
267
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400268 try {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400269 deleteDefaultChannelIfNeededLocked(r);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400270 } catch (PackageManager.NameNotFoundException e) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400271 Slog.e(TAG, "deleteDefaultChannelIfNeededLocked - Exception: " + e);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400272 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400273 }
274 }
275 }
276 }
277 }
278 throw new IllegalStateException("Failed to reach END_DOCUMENT");
279 }
280
Julia Reynolds5c399c62019-04-08 14:42:53 -0400281 private PackagePreferences getPackagePreferencesLocked(String pkg, int uid) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400282 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400283 return mPackagePreferences.get(key);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400284 }
285
Julia Reynolds5c399c62019-04-08 14:42:53 -0400286 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, int uid) {
287 return getOrCreatePackagePreferencesLocked(pkg, uid,
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500288 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800289 DEFAULT_ALLOW_BUBBLE);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400290 }
291
Julia Reynolds5c399c62019-04-08 14:42:53 -0400292 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, int uid,
293 int importance, int priority, int visibility, boolean showBadge, boolean allowBubble) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400294 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400295 PackagePreferences
296 r = (uid == UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
297 : mPackagePreferences.get(key);
298 if (r == null) {
299 r = new PackagePreferences();
300 r.pkg = pkg;
301 r.uid = uid;
302 r.importance = importance;
303 r.priority = priority;
304 r.visibility = visibility;
305 r.showBadge = showBadge;
306 r.allowBubble = allowBubble;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400307
Julia Reynolds5c399c62019-04-08 14:42:53 -0400308 try {
309 createDefaultChannelIfNeededLocked(r);
310 } catch (PackageManager.NameNotFoundException e) {
311 Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400312 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400313
314 if (r.uid == UNKNOWN_UID) {
315 mRestoredWithoutUids.put(pkg, r);
316 } else {
317 mPackagePreferences.put(key, r);
318 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400319 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400320 return r;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400321 }
322
323 private boolean shouldHaveDefaultChannel(PackagePreferences r) throws
324 PackageManager.NameNotFoundException {
325 final int userId = UserHandle.getUserId(r.uid);
326 final ApplicationInfo applicationInfo =
327 mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
328 if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
329 // O apps should not have the default channel.
330 return false;
331 }
332
333 // Otherwise, this app should have the default channel.
334 return true;
335 }
336
Julia Reynolds5c399c62019-04-08 14:42:53 -0400337 private void deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400338 PackageManager.NameNotFoundException {
339 if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
340 // Not present
341 return;
342 }
343
344 if (shouldHaveDefaultChannel(r)) {
345 // Keep the default channel until upgraded.
346 return;
347 }
348
349 // Remove Default Channel.
350 r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
351 }
352
Julia Reynolds5c399c62019-04-08 14:42:53 -0400353 private void createDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400354 PackageManager.NameNotFoundException {
355 if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
356 r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
357 com.android.internal.R.string.default_notification_channel_label));
358 return;
359 }
360
361 if (!shouldHaveDefaultChannel(r)) {
362 // Keep the default channel until upgraded.
363 return;
364 }
365
366 // Create Default Channel
367 NotificationChannel channel;
368 channel = new NotificationChannel(
369 NotificationChannel.DEFAULT_CHANNEL_ID,
370 mContext.getString(R.string.default_notification_channel_label),
371 r.importance);
372 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
373 channel.setLockscreenVisibility(r.visibility);
374 if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
375 channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
376 }
377 if (r.priority != DEFAULT_PRIORITY) {
378 channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
379 }
380 if (r.visibility != DEFAULT_VISIBILITY) {
381 channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
382 }
383 r.channels.put(channel.getId(), channel);
384 }
385
Annie Meng8b646fd2019-02-01 18:46:42 +0000386 public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400387 out.startTag(null, TAG_RANKING);
388 out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
Annie Meng8b646fd2019-02-01 18:46:42 +0000389 if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS
390 && (!forBackup || userId == UserHandle.USER_SYSTEM)) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500391 out.startTag(null, TAG_STATUS_ICONS);
392 out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons));
393 out.endTag(null, TAG_STATUS_ICONS);
394 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400395
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400396 synchronized (mPackagePreferences) {
397 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400398 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400399 final PackagePreferences r = mPackagePreferences.valueAt(i);
Annie Meng8b646fd2019-02-01 18:46:42 +0000400 if (forBackup && UserHandle.getUserId(r.uid) != userId) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400401 continue;
402 }
403 final boolean hasNonDefaultSettings =
404 r.importance != DEFAULT_IMPORTANCE
405 || r.priority != DEFAULT_PRIORITY
406 || r.visibility != DEFAULT_VISIBILITY
407 || r.showBadge != DEFAULT_SHOW_BADGE
408 || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
409 || r.channels.size() > 0
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400410 || r.groups.size() > 0
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500411 || r.delegate != null
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800412 || r.allowBubble != DEFAULT_ALLOW_BUBBLE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400413 if (hasNonDefaultSettings) {
414 out.startTag(null, TAG_PACKAGE);
415 out.attribute(null, ATT_NAME, r.pkg);
416 if (r.importance != DEFAULT_IMPORTANCE) {
417 out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
418 }
419 if (r.priority != DEFAULT_PRIORITY) {
420 out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
421 }
422 if (r.visibility != DEFAULT_VISIBILITY) {
423 out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
424 }
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800425 if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) {
426 out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble));
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500427 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400428 out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
429 out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
430 Integer.toString(r.lockedAppFields));
431
432 if (!forBackup) {
433 out.attribute(null, ATT_UID, Integer.toString(r.uid));
434 }
435
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400436 if (r.delegate != null) {
437 out.startTag(null, TAG_DELEGATE);
438
439 out.attribute(null, ATT_NAME, r.delegate.mPkg);
440 out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
441 if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
442 out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
443 }
444 if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
445 out.attribute(null, ATT_USER_ALLOWED,
446 Boolean.toString(r.delegate.mUserAllowed));
447 }
448 out.endTag(null, TAG_DELEGATE);
449 }
450
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400451 for (NotificationChannelGroup group : r.groups.values()) {
452 group.writeXml(out);
453 }
454
455 for (NotificationChannel channel : r.channels.values()) {
456 if (forBackup) {
457 if (!channel.isDeleted()) {
458 channel.writeXmlForBackup(out, mContext);
459 }
460 } else {
461 channel.writeXml(out);
462 }
463 }
464
465 out.endTag(null, TAG_PACKAGE);
466 }
467 }
468 }
469 out.endTag(null, TAG_RANKING);
470 }
471
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800472 /**
473 * Sets whether bubbles are allowed.
474 *
475 * @param pkg the package to allow or not allow bubbles for.
476 * @param uid the uid to allow or not allow bubbles for.
477 * @param allowed whether bubbles are allowed.
478 */
479 public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400480 synchronized (mPackagePreferences) {
481 PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid);
482 p.allowBubble = allowed;
483 p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
484 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500485 }
486
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800487 /**
488 * Whether bubbles are allowed.
489 *
490 * @param pkg the package to check if bubbles are allowed for
491 * @param uid the uid to check if bubbles are allowed for.
492 * @return whether bubbles are allowed.
493 */
Julia Reynolds4509ce72019-01-31 13:12:43 -0500494 @Override
Mady Mellor9db685a2019-01-23 13:23:37 -0800495 public boolean areBubblesAllowed(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400496 synchronized (mPackagePreferences) {
497 return getOrCreatePackagePreferencesLocked(pkg, uid).allowBubble;
498 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500499 }
500
501 public int getAppLockedFields(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400502 synchronized (mPackagePreferences) {
503 return getOrCreatePackagePreferencesLocked(pkg, uid).lockedAppFields;
504 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500505 }
506
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400507 /**
508 * Gets importance.
509 */
510 @Override
511 public int getImportance(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400512 synchronized (mPackagePreferences) {
513 return getOrCreatePackagePreferencesLocked(packageName, uid).importance;
514 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400515 }
516
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400517 /**
518 * Returns whether the importance of the corresponding notification is user-locked and shouldn't
519 * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
520 * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
521 */
522 public boolean getIsAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400523 synchronized (mPackagePreferences) {
524 int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields;
525 return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
526 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400527 }
528
529 @Override
530 public boolean canShowBadge(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400531 synchronized (mPackagePreferences) {
532 return getOrCreatePackagePreferencesLocked(packageName, uid).showBadge;
533 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400534 }
535
536 @Override
537 public void setShowBadge(String packageName, int uid, boolean showBadge) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400538 synchronized (mPackagePreferences) {
539 getOrCreatePackagePreferencesLocked(packageName, uid).showBadge = showBadge;
540 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400541 updateConfig();
542 }
543
544 @Override
545 public boolean isGroupBlocked(String packageName, int uid, String groupId) {
546 if (groupId == null) {
547 return false;
548 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400549 synchronized (mPackagePreferences) {
550 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
551 NotificationChannelGroup group = r.groups.get(groupId);
552 if (group == null) {
553 return false;
554 }
555 return group.isBlocked();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400556 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400557 }
558
559 int getPackagePriority(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400560 synchronized (mPackagePreferences) {
561 return getOrCreatePackagePreferencesLocked(pkg, uid).priority;
562 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400563 }
564
565 int getPackageVisibility(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400566 synchronized (mPackagePreferences) {
567 return getOrCreatePackagePreferencesLocked(pkg, uid).visibility;
568 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400569 }
570
571 @Override
572 public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
573 boolean fromTargetApp) {
574 Preconditions.checkNotNull(pkg);
575 Preconditions.checkNotNull(group);
576 Preconditions.checkNotNull(group.getId());
577 Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
Julia Reynolds5c399c62019-04-08 14:42:53 -0400578 synchronized (mPackagePreferences) {
579 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
580 if (r == null) {
581 throw new IllegalArgumentException("Invalid package");
582 }
583 final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
584 if (!group.equals(oldGroup)) {
585 // will log for new entries as well as name/description changes
586 MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
587 }
588 if (oldGroup != null) {
589 group.setChannels(oldGroup.getChannels());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400590
Julia Reynolds5c399c62019-04-08 14:42:53 -0400591 // apps can't update the blocked status or app overlay permission
592 if (fromTargetApp) {
593 group.setBlocked(oldGroup.isBlocked());
594 group.unlockFields(group.getUserLockedFields());
595 group.lockFields(oldGroup.getUserLockedFields());
596 } else {
597 // but the system can
598 if (group.isBlocked() != oldGroup.isBlocked()) {
599 group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
600 updateChannelsBypassingDnd(mContext.getUserId());
601 }
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -0400602 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400603 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400604 r.groups.put(group.getId(), group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400605 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400606 }
607
608 @Override
609 public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
610 boolean fromTargetApp, boolean hasDndAccess) {
611 Preconditions.checkNotNull(pkg);
612 Preconditions.checkNotNull(channel);
613 Preconditions.checkNotNull(channel.getId());
614 Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
Julia Reynolds5c399c62019-04-08 14:42:53 -0400615 synchronized (mPackagePreferences) {
616 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
617 if (r == null) {
618 throw new IllegalArgumentException("Invalid package");
619 }
620 if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
621 throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
622 }
623 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
624 throw new IllegalArgumentException("Reserved id");
625 }
626 NotificationChannel existing = r.channels.get(channel.getId());
627 // Keep most of the existing settings
628 if (existing != null && fromTargetApp) {
629 if (existing.isDeleted()) {
630 existing.setDeleted(false);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400631
Julia Reynolds5c399c62019-04-08 14:42:53 -0400632 // log a resurrected channel as if it's new again
633 MetricsLogger.action(getChannelLog(channel, pkg).setType(
634 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
635 }
636
637 existing.setName(channel.getName().toString());
638 existing.setDescription(channel.getDescription());
639 existing.setBlockableSystem(channel.isBlockableSystem());
640 if (existing.getGroup() == null) {
641 existing.setGroup(channel.getGroup());
642 }
643
644 // Apps are allowed to downgrade channel importance if the user has not changed any
645 // fields on this channel yet.
646 final int previousExistingImportance = existing.getImportance();
647 if (existing.getUserLockedFields() == 0 &&
648 channel.getImportance() < existing.getImportance()) {
649 existing.setImportance(channel.getImportance());
650 }
651
652 // system apps and dnd access apps can bypass dnd if the user hasn't changed any
653 // fields on the channel yet
654 if (existing.getUserLockedFields() == 0 && hasDndAccess) {
655 boolean bypassDnd = channel.canBypassDnd();
656 existing.setBypassDnd(bypassDnd);
657
658 if (bypassDnd != mAreChannelsBypassingDnd
659 || previousExistingImportance != existing.getImportance()) {
660 updateChannelsBypassingDnd(mContext.getUserId());
661 }
662 }
663
664 updateConfig();
665 return;
666 }
667 if (channel.getImportance() < IMPORTANCE_NONE
668 || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
669 throw new IllegalArgumentException("Invalid importance level");
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400670 }
671
Julia Reynolds5c399c62019-04-08 14:42:53 -0400672 // Reset fields that apps aren't allowed to set.
673 if (fromTargetApp && !hasDndAccess) {
674 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400675 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400676 if (fromTargetApp) {
677 channel.setLockscreenVisibility(r.visibility);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400678 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400679 clearLockedFieldsLocked(channel);
680 channel.setImportanceLockedByOEM(r.oemLockedImportance);
681 if (!channel.isImportanceLockedByOEM()) {
682 if (r.futureOemLockedChannels.remove(channel.getId())) {
683 channel.setImportanceLockedByOEM(true);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400684 }
685 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400686 channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
687 if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
688 channel.setLockscreenVisibility(
689 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
Julia Reynolds413ba842019-01-11 10:38:08 -0500690 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400691 if (!r.showBadge) {
692 channel.setShowBadge(false);
693 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400694
Julia Reynolds5c399c62019-04-08 14:42:53 -0400695 r.channels.put(channel.getId(), channel);
696 if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
697 updateChannelsBypassingDnd(mContext.getUserId());
698 }
699 MetricsLogger.action(getChannelLog(channel, pkg).setType(
700 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400701 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400702 }
703
Julia Reynolds5c399c62019-04-08 14:42:53 -0400704 void clearLockedFieldsLocked(NotificationChannel channel) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400705 channel.unlockFields(channel.getUserLockedFields());
706 }
707
708 @Override
709 public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
710 boolean fromUser) {
711 Preconditions.checkNotNull(updatedChannel);
712 Preconditions.checkNotNull(updatedChannel.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400713 synchronized (mPackagePreferences) {
714 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
715 if (r == null) {
716 throw new IllegalArgumentException("Invalid package");
717 }
718 NotificationChannel channel = r.channels.get(updatedChannel.getId());
719 if (channel == null || channel.isDeleted()) {
720 throw new IllegalArgumentException("Channel does not exist");
721 }
722 if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
723 updatedChannel.setLockscreenVisibility(
724 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
725 }
726 if (fromUser) {
727 updatedChannel.lockFields(channel.getUserLockedFields());
728 lockFieldsForUpdateLocked(channel, updatedChannel);
729 } else {
730 updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
731 }
732 // no importance updates are allowed if OEM blocked it
733 updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
734 if (updatedChannel.isImportanceLockedByOEM()) {
735 updatedChannel.setImportance(channel.getImportance());
736 }
737 updatedChannel.setImportanceLockedByCriticalDeviceFunction(
738 r.defaultAppLockedImportance);
Beverly47679222019-05-16 15:46:11 -0400739 if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
740 && updatedChannel.getImportance() == IMPORTANCE_NONE) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400741 updatedChannel.setImportance(channel.getImportance());
742 }
Julia Reynolds413ba842019-01-11 10:38:08 -0500743
Julia Reynolds5c399c62019-04-08 14:42:53 -0400744 r.channels.put(updatedChannel.getId(), updatedChannel);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400745
Julia Reynolds5c399c62019-04-08 14:42:53 -0400746 if (onlyHasDefaultChannel(pkg, uid)) {
747 // copy settings to app level so they are inherited by new channels
748 // when the app migrates
749 r.importance = updatedChannel.getImportance();
750 r.priority = updatedChannel.canBypassDnd()
751 ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
752 r.visibility = updatedChannel.getLockscreenVisibility();
753 r.showBadge = updatedChannel.canShowBadge();
754 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400755
Julia Reynolds5c399c62019-04-08 14:42:53 -0400756 if (!channel.equals(updatedChannel)) {
757 // only log if there are real changes
758 MetricsLogger.action(getChannelLog(updatedChannel, pkg)
759 .setSubtype(fromUser ? 1 : 0));
760 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400761
Julia Reynolds5c399c62019-04-08 14:42:53 -0400762 if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
763 || channel.getImportance() != updatedChannel.getImportance()) {
764 updateChannelsBypassingDnd(mContext.getUserId());
765 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400766 }
767 updateConfig();
768 }
769
770 @Override
771 public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
772 boolean includeDeleted) {
773 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400774 synchronized (mPackagePreferences) {
775 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
776 if (r == null) {
777 return null;
778 }
779 if (channelId == null) {
780 channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
781 }
782 final NotificationChannel nc = r.channels.get(channelId);
783 if (nc != null && (includeDeleted || !nc.isDeleted())) {
784 return nc;
785 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400786 return null;
787 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400788 }
789
790 @Override
791 public void deleteNotificationChannel(String pkg, int uid, String channelId) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400792 synchronized (mPackagePreferences) {
793 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
794 if (r == null) {
795 return;
796 }
797 NotificationChannel channel = r.channels.get(channelId);
798 if (channel != null) {
799 channel.setDeleted(true);
800 LogMaker lm = getChannelLog(channel, pkg);
801 lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
802 MetricsLogger.action(lm);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400803
Julia Reynolds5c399c62019-04-08 14:42:53 -0400804 if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
805 updateChannelsBypassingDnd(mContext.getUserId());
806 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400807 }
808 }
809 }
810
811 @Override
812 @VisibleForTesting
813 public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
814 Preconditions.checkNotNull(pkg);
815 Preconditions.checkNotNull(channelId);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400816 synchronized (mPackagePreferences) {
817 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
818 if (r == null) {
819 return;
820 }
821 r.channels.remove(channelId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400822 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400823 }
824
825 @Override
826 public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
827 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400828 synchronized (mPackagePreferences) {
829 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
830 if (r == null) {
831 return;
832 }
833 int N = r.channels.size() - 1;
834 for (int i = N; i >= 0; i--) {
835 String key = r.channels.keyAt(i);
836 if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
837 r.channels.remove(key);
838 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400839 }
840 }
841 }
842
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500843 public boolean shouldHideSilentStatusIcons() {
844 return mHideSilentStatusBarIcons;
845 }
846
847 public void setHideSilentStatusIcons(boolean hide) {
848 mHideSilentStatusBarIcons = hide;
849 }
850
Julia Reynolds413ba842019-01-11 10:38:08 -0500851 public void lockChannelsForOEM(String[] appOrChannelList) {
852 if (appOrChannelList == null) {
853 return;
854 }
855 for (String appOrChannel : appOrChannelList) {
856 if (!TextUtils.isEmpty(appOrChannel)) {
857 String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
858 if (appSplit != null && appSplit.length > 0) {
859 String appName = appSplit[0];
860 String channelId = appSplit.length == 2 ? appSplit[1] : null;
861
862 synchronized (mPackagePreferences) {
863 for (PackagePreferences r : mPackagePreferences.values()) {
864 if (r.pkg.equals(appName)) {
865 if (channelId == null) {
866 // lock all channels for the app
867 r.oemLockedImportance = true;
868 for (NotificationChannel channel : r.channels.values()) {
869 channel.setImportanceLockedByOEM(true);
870 }
871 } else {
872 NotificationChannel channel = r.channels.get(channelId);
873 if (channel != null) {
874 channel.setImportanceLockedByOEM(true);
875 } else {
876 // if this channel shows up in the future, make sure it'll
877 // be locked immediately
878 r.futureOemLockedChannels.add(channelId);
879 }
880 }
881 }
882 }
883 }
884 }
885 }
886 }
887 }
888
Julia Reynoldse7ca31b2019-04-25 15:41:47 -0400889 public void updateDefaultApps(int userId, ArraySet<String> toRemove,
890 ArraySet<Pair<String, Integer>> toAdd) {
Julia Reynolds0c245002019-03-27 16:10:11 -0400891 synchronized (mPackagePreferences) {
892 for (PackagePreferences p : mPackagePreferences.values()) {
893 if (userId == UserHandle.getUserId(p.uid)) {
894 if (toRemove != null && toRemove.contains(p.pkg)) {
895 p.defaultAppLockedImportance = false;
896 for (NotificationChannel channel : p.channels.values()) {
897 channel.setImportanceLockedByCriticalDeviceFunction(false);
898 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -0400899 }
900 }
901 }
902 if (toAdd != null) {
903 for (Pair<String, Integer> approvedApp : toAdd) {
904 PackagePreferences p = getOrCreatePackagePreferencesLocked(approvedApp.first,
905 approvedApp.second);
906 p.defaultAppLockedImportance = true;
907 for (NotificationChannel channel : p.channels.values()) {
908 channel.setImportanceLockedByCriticalDeviceFunction(true);
Julia Reynolds0c245002019-03-27 16:10:11 -0400909 }
910 }
911 }
912 }
913 }
914
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400915 public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
916 int uid, String groupId, boolean includeDeleted) {
917 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400918 synchronized (mPackagePreferences) {
919 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
920 if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
921 return null;
922 }
923 NotificationChannelGroup group = r.groups.get(groupId).clone();
924 group.setChannels(new ArrayList<>());
925 int N = r.channels.size();
926 for (int i = 0; i < N; i++) {
927 final NotificationChannel nc = r.channels.valueAt(i);
928 if (includeDeleted || !nc.isDeleted()) {
929 if (groupId.equals(nc.getGroup())) {
930 group.addChannel(nc);
931 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400932 }
933 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400934 return group;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400935 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400936 }
937
938 public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
939 int uid) {
940 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400941 synchronized (mPackagePreferences) {
942 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
943 if (r == null) {
944 return null;
945 }
946 return r.groups.get(groupId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400947 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400948 }
949
950 @Override
951 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
Julia Reynolds13ed28b2018-09-21 15:20:13 -0400952 int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400953 Preconditions.checkNotNull(pkg);
954 Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -0400955 synchronized (mPackagePreferences) {
956 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
957 if (r == null) {
958 return ParceledListSlice.emptyList();
959 }
960 NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
961 int N = r.channels.size();
962 for (int i = 0; i < N; i++) {
963 final NotificationChannel nc = r.channels.valueAt(i);
964 if (includeDeleted || !nc.isDeleted()) {
965 if (nc.getGroup() != null) {
966 if (r.groups.get(nc.getGroup()) != null) {
967 NotificationChannelGroup ncg = groups.get(nc.getGroup());
968 if (ncg == null) {
969 ncg = r.groups.get(nc.getGroup()).clone();
970 ncg.setChannels(new ArrayList<>());
971 groups.put(nc.getGroup(), ncg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400972
Julia Reynolds5c399c62019-04-08 14:42:53 -0400973 }
974 ncg.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400975 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400976 } else {
977 nonGrouped.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400978 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400979 }
980 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400981 if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
982 groups.put(null, nonGrouped);
983 }
984 if (includeEmpty) {
985 for (NotificationChannelGroup group : r.groups.values()) {
986 if (!groups.containsKey(group.getId())) {
987 groups.put(group.getId(), group);
988 }
Julia Reynolds13ed28b2018-09-21 15:20:13 -0400989 }
990 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400991 return new ParceledListSlice<>(new ArrayList<>(groups.values()));
Julia Reynolds13ed28b2018-09-21 15:20:13 -0400992 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400993 }
994
995 public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
996 String groupId) {
997 List<NotificationChannel> deletedChannels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -0400998 synchronized (mPackagePreferences) {
999 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1000 if (r == null || TextUtils.isEmpty(groupId)) {
1001 return deletedChannels;
1002 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001003
Julia Reynolds5c399c62019-04-08 14:42:53 -04001004 r.groups.remove(groupId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001005
Julia Reynolds5c399c62019-04-08 14:42:53 -04001006 int N = r.channels.size();
1007 for (int i = 0; i < N; i++) {
1008 final NotificationChannel nc = r.channels.valueAt(i);
1009 if (groupId.equals(nc.getGroup())) {
1010 nc.setDeleted(true);
1011 deletedChannels.add(nc);
1012 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001013 }
1014 }
1015 return deletedChannels;
1016 }
1017
1018 @Override
1019 public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
1020 int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001021 List<NotificationChannelGroup> groups = new ArrayList<>();
1022 synchronized (mPackagePreferences) {
1023 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1024 if (r == null) {
1025 return groups;
1026 }
1027 groups.addAll(r.groups.values());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001028 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001029 return groups;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001030 }
1031
1032 @Override
1033 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
1034 boolean includeDeleted) {
1035 Preconditions.checkNotNull(pkg);
1036 List<NotificationChannel> channels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001037 synchronized (mPackagePreferences) {
1038 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1039 if (r == null) {
1040 return ParceledListSlice.emptyList();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001041 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001042 int N = r.channels.size();
1043 for (int i = 0; i < N; i++) {
1044 final NotificationChannel nc = r.channels.valueAt(i);
1045 if (includeDeleted || !nc.isDeleted()) {
1046 channels.add(nc);
1047 }
1048 }
1049 return new ParceledListSlice<>(channels);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001050 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001051 }
1052
1053 /**
Beverly0479cde22018-11-09 11:05:34 -05001054 * Gets all notification channels associated with the given pkg and userId that can bypass dnd
1055 */
1056 public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
1057 int userId) {
1058 List<NotificationChannel> channels = new ArrayList<>();
1059 synchronized (mPackagePreferences) {
1060 final PackagePreferences r = mPackagePreferences.get(
1061 packagePreferencesKey(pkg, userId));
1062 // notifications from this package aren't blocked
1063 if (r != null && r.importance != IMPORTANCE_NONE) {
1064 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001065 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001066 channels.add(channel);
1067 }
1068 }
1069 }
1070 }
1071 return new ParceledListSlice<>(channels);
1072 }
1073
1074 /**
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001075 * True for pre-O apps that only have the default channel, or pre O apps that have no
1076 * channels yet. This method will create the default channel for pre-O apps that don't have it.
1077 * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
1078 * upgrades.
1079 */
1080 public boolean onlyHasDefaultChannel(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001081 synchronized (mPackagePreferences) {
1082 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1083 if (r.channels.size() == 1
1084 && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
1085 return true;
1086 }
1087 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001088 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001089 }
1090
1091 public int getDeletedChannelCount(String pkg, int uid) {
1092 Preconditions.checkNotNull(pkg);
1093 int deletedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001094 synchronized (mPackagePreferences) {
1095 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1096 if (r == null) {
1097 return deletedCount;
1098 }
1099 int N = r.channels.size();
1100 for (int i = 0; i < N; i++) {
1101 final NotificationChannel nc = r.channels.valueAt(i);
1102 if (nc.isDeleted()) {
1103 deletedCount++;
1104 }
1105 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001106 return deletedCount;
1107 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001108 }
1109
1110 public int getBlockedChannelCount(String pkg, int uid) {
1111 Preconditions.checkNotNull(pkg);
1112 int blockedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001113 synchronized (mPackagePreferences) {
1114 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1115 if (r == null) {
1116 return blockedCount;
1117 }
1118 int N = r.channels.size();
1119 for (int i = 0; i < N; i++) {
1120 final NotificationChannel nc = r.channels.valueAt(i);
1121 if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
1122 blockedCount++;
1123 }
1124 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001125 return blockedCount;
1126 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001127 }
1128
1129 public int getBlockedAppCount(int userId) {
1130 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001131 synchronized (mPackagePreferences) {
1132 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001133 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001134 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001135 if (userId == UserHandle.getUserId(r.uid)
1136 && r.importance == IMPORTANCE_NONE) {
1137 count++;
1138 }
1139 }
1140 }
1141 return count;
1142 }
1143
Beverly0479cde22018-11-09 11:05:34 -05001144 /**
1145 * Returns the number of apps that have at least one notification channel that can bypass DND
1146 * for given particular user
1147 */
1148 public int getAppsBypassingDndCount(int userId) {
1149 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001150 synchronized (mPackagePreferences) {
Beverly0479cde22018-11-09 11:05:34 -05001151 final int numPackagePreferences = mPackagePreferences.size();
1152 for (int i = 0; i < numPackagePreferences; i++) {
1153 final PackagePreferences r = mPackagePreferences.valueAt(i);
1154 // Package isn't associated with this userId or notifications from this package are
1155 // blocked
1156 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1157 continue;
1158 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001159
Beverly0479cde22018-11-09 11:05:34 -05001160 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001161 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001162 count++;
1163 break;
1164 }
1165 }
1166 }
1167 }
1168 return count;
1169 }
1170
1171 /**
1172 * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
1173 * updating
1174 * @param userId
1175 */
1176 private void syncChannelsBypassingDnd(int userId) {
1177 mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
1178 & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
1179 updateChannelsBypassingDnd(userId);
1180 }
1181
1182 /**
1183 * Updates the user's NotificationPolicy based on whether the given userId
1184 * has channels bypassing DND
1185 * @param userId
1186 */
1187 private void updateChannelsBypassingDnd(int userId) {
1188 synchronized (mPackagePreferences) {
1189 final int numPackagePreferences = mPackagePreferences.size();
1190 for (int i = 0; i < numPackagePreferences; i++) {
1191 final PackagePreferences r = mPackagePreferences.valueAt(i);
1192 // Package isn't associated with this userId or notifications from this package are
1193 // blocked
1194 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1195 continue;
1196 }
1197
1198 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001199 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001200 if (!mAreChannelsBypassingDnd) {
1201 mAreChannelsBypassingDnd = true;
1202 updateZenPolicy(true);
1203 }
1204 return;
1205 }
1206 }
1207 }
1208 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001209 // If no channels bypass DND, update the zen policy once to disable DND bypass.
1210 if (mAreChannelsBypassingDnd) {
1211 mAreChannelsBypassingDnd = false;
1212 updateZenPolicy(false);
1213 }
1214 }
1215
Julia Reynolds5c399c62019-04-08 14:42:53 -04001216 private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
Beverly0479cde22018-11-09 11:05:34 -05001217 // Channel is in a group that's blocked
Beverly4f7b53d2018-11-20 09:56:31 -05001218 if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
1219 return false;
Beverly0479cde22018-11-09 11:05:34 -05001220 }
1221
1222 // Channel is deleted or is blocked
1223 if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
1224 return false;
1225 }
1226
1227 return true;
1228 }
1229
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001230 public void updateZenPolicy(boolean areChannelsBypassingDnd) {
1231 NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
1232 mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
1233 policy.priorityCategories, policy.priorityCallSenders,
1234 policy.priorityMessageSenders, policy.suppressedVisualEffects,
1235 (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
1236 : 0)));
1237 }
1238
1239 public boolean areChannelsBypassingDnd() {
1240 return mAreChannelsBypassingDnd;
1241 }
1242
1243 /**
1244 * Sets importance.
1245 */
1246 @Override
1247 public void setImportance(String pkgName, int uid, int importance) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001248 synchronized (mPackagePreferences) {
1249 getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance;
1250 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001251 updateConfig();
1252 }
1253
1254 public void setEnabled(String packageName, int uid, boolean enabled) {
1255 boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
1256 if (wasEnabled == enabled) {
1257 return;
1258 }
1259 setImportance(packageName, uid,
1260 enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
1261 }
1262
1263 /**
1264 * Sets whether any notifications from the app, represented by the given {@code pkgName} and
1265 * {@code uid}, have their importance locked by the user. Locked notifications don't get
1266 * considered for sentiment adjustments (and thus never show a blocking helper).
1267 */
1268 public void setAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001269 synchronized (mPackagePreferences) {
1270 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(packageName, uid);
1271 if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
1272 return;
1273 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001274
Julia Reynolds5c399c62019-04-08 14:42:53 -04001275 prefs.lockedAppFields =
1276 prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
1277 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001278 updateConfig();
1279 }
1280
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001281 /**
1282 * Returns the delegate for a given package, if it's allowed by the package and the user.
1283 */
1284 public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001285 synchronized (mPackagePreferences) {
1286 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001287
Julia Reynolds5c399c62019-04-08 14:42:53 -04001288 if (prefs == null || prefs.delegate == null) {
1289 return null;
1290 }
1291 if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
1292 return null;
1293 }
1294 return prefs.delegate.mPkg;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001295 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001296 }
1297
1298 /**
1299 * Used by an app to delegate notification posting privileges to another apps.
1300 */
1301 public void setNotificationDelegate(String sourcePkg, int sourceUid,
1302 String delegatePkg, int delegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001303 synchronized (mPackagePreferences) {
1304 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001305
Julia Reynolds5c399c62019-04-08 14:42:53 -04001306 boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
1307 Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
1308 prefs.delegate = delegate;
1309 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001310 updateConfig();
1311 }
1312
1313 /**
1314 * Used by an app to turn off its notification delegate.
1315 */
1316 public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001317 boolean changed = false;
1318 synchronized (mPackagePreferences) {
1319 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1320 if (prefs != null && prefs.delegate != null) {
1321 prefs.delegate.mEnabled = false;
1322 changed = true;
1323 }
1324 }
1325 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001326 updateConfig();
1327 }
1328 }
1329
1330 /**
1331 * Toggles whether an app can have a notification delegate on behalf of a user.
1332 */
1333 public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001334 boolean changed = false;
1335 synchronized (mPackagePreferences) {
1336 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1337 if (prefs != null && prefs.delegate != null) {
1338 prefs.delegate.mUserAllowed = userAllowed;
1339 changed = true;
1340 }
1341 }
1342 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001343 updateConfig();
1344 }
1345 }
1346
1347 /**
1348 * Returns whether the given app is allowed on post notifications on behalf of the other given
1349 * app.
1350 */
1351 public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
1352 String potentialDelegatePkg, int potentialDelegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001353 synchronized (mPackagePreferences) {
1354 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001355
Julia Reynolds5c399c62019-04-08 14:42:53 -04001356 return prefs != null && prefs.isValidDelegate(potentialDelegatePkg,
1357 potentialDelegateUid);
1358 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001359 }
1360
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001361 @VisibleForTesting
Julia Reynolds5c399c62019-04-08 14:42:53 -04001362 void lockFieldsForUpdateLocked(NotificationChannel original, NotificationChannel update) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001363 if (original.canBypassDnd() != update.canBypassDnd()) {
1364 update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
1365 }
1366 if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
1367 update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
1368 }
1369 if (original.getImportance() != update.getImportance()) {
1370 update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
1371 }
1372 if (original.shouldShowLights() != update.shouldShowLights()
1373 || original.getLightColor() != update.getLightColor()) {
1374 update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
1375 }
1376 if (!Objects.equals(original.getSound(), update.getSound())) {
1377 update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
1378 }
1379 if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
1380 || original.shouldVibrate() != update.shouldVibrate()) {
1381 update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
1382 }
1383 if (original.canShowBadge() != update.canShowBadge()) {
1384 update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
1385 }
Julia Reynolds4509ce72019-01-31 13:12:43 -05001386 if (original.canBubble() != update.canBubble()) {
Mady Mellorc39b4ae2019-01-09 17:11:37 -08001387 update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE);
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -04001388 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001389 }
1390
1391 public void dump(PrintWriter pw, String prefix,
1392 @NonNull NotificationManagerService.DumpFilter filter) {
1393 pw.print(prefix);
1394 pw.println("per-package config:");
1395
Julia Reynolds5c399c62019-04-08 14:42:53 -04001396 pw.println("PackagePreferences:");
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001397 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001398 dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001399 }
1400 pw.println("Restored without uid:");
Julia Reynolds5c399c62019-04-08 14:42:53 -04001401 dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001402 }
1403
1404 public void dump(ProtoOutputStream proto,
1405 @NonNull NotificationManagerService.DumpFilter filter) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001406 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001407 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001408 mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001409 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001410 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001411 mRestoredWithoutUids);
1412 }
1413
Julia Reynolds5c399c62019-04-08 14:42:53 -04001414 private static void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001415 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001416 ArrayMap<String, PackagePreferences> packagePreferences) {
1417 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001418 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001419 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001420 if (filter.matches(r.pkg)) {
1421 pw.print(prefix);
1422 pw.print(" AppSettings: ");
1423 pw.print(r.pkg);
1424 pw.print(" (");
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001425 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001426 pw.print(')');
1427 if (r.importance != DEFAULT_IMPORTANCE) {
1428 pw.print(" importance=");
1429 pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
1430 }
1431 if (r.priority != DEFAULT_PRIORITY) {
1432 pw.print(" priority=");
1433 pw.print(Notification.priorityToString(r.priority));
1434 }
1435 if (r.visibility != DEFAULT_VISIBILITY) {
1436 pw.print(" visibility=");
1437 pw.print(Notification.visibilityToString(r.visibility));
1438 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001439 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1440 pw.print(" showBadge=");
1441 pw.print(r.showBadge);
1442 }
1443 if (r.defaultAppLockedImportance != DEFAULT_APP_LOCKED_IMPORTANCE) {
1444 pw.print(" defaultAppLocked=");
1445 pw.print(r.defaultAppLockedImportance);
1446 }
1447 if (r.oemLockedImportance != DEFAULT_OEM_LOCKED_IMPORTANCE) {
1448 pw.print(" oemLocked=");
1449 pw.print(r.oemLockedImportance);
1450 }
1451 if (!r.futureOemLockedChannels.isEmpty()) {
1452 pw.print(" futureLockedChannels=");
1453 pw.print(r.futureOemLockedChannels);
1454 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001455 pw.println();
1456 for (NotificationChannel channel : r.channels.values()) {
1457 pw.print(prefix);
1458 channel.dump(pw, " ", filter.redact);
1459 }
1460 for (NotificationChannelGroup group : r.groups.values()) {
1461 pw.print(prefix);
1462 pw.print(" ");
1463 pw.print(" ");
1464 pw.println(group);
1465 }
1466 }
1467 }
1468 }
1469
Julia Reynolds5c399c62019-04-08 14:42:53 -04001470 private static void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001471 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001472 ArrayMap<String, PackagePreferences> packagePreferences) {
1473 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001474 long fToken;
1475 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001476 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001477 if (filter.matches(r.pkg)) {
1478 fToken = proto.start(fieldId);
1479
1480 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
1481 proto.write(RankingHelperProto.RecordProto.UID, r.uid);
1482 proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
1483 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
1484 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
1485 proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
1486
1487 for (NotificationChannel channel : r.channels.values()) {
1488 channel.writeToProto(proto, RankingHelperProto.RecordProto.CHANNELS);
1489 }
1490 for (NotificationChannelGroup group : r.groups.values()) {
1491 group.writeToProto(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS);
1492 }
1493
1494 proto.end(fToken);
1495 }
1496 }
1497 }
1498
1499 public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
1500 JSONObject ranking = new JSONObject();
1501 JSONArray PackagePreferencess = new JSONArray();
1502 try {
1503 ranking.put("noUid", mRestoredWithoutUids.size());
1504 } catch (JSONException e) {
1505 // pass
1506 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001507 synchronized (mPackagePreferences) {
1508 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001509 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001510 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001511 if (filter == null || filter.matches(r.pkg)) {
1512 JSONObject PackagePreferences = new JSONObject();
1513 try {
1514 PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
1515 PackagePreferences.put("packageName", r.pkg);
1516 if (r.importance != DEFAULT_IMPORTANCE) {
1517 PackagePreferences.put("importance",
1518 NotificationListenerService.Ranking.importanceToString(
1519 r.importance));
1520 }
1521 if (r.priority != DEFAULT_PRIORITY) {
1522 PackagePreferences.put("priority",
1523 Notification.priorityToString(r.priority));
1524 }
1525 if (r.visibility != DEFAULT_VISIBILITY) {
1526 PackagePreferences.put("visibility",
1527 Notification.visibilityToString(r.visibility));
1528 }
1529 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1530 PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
1531 }
1532 JSONArray channels = new JSONArray();
1533 for (NotificationChannel channel : r.channels.values()) {
1534 channels.put(channel.toJson());
1535 }
1536 PackagePreferences.put("channels", channels);
1537 JSONArray groups = new JSONArray();
1538 for (NotificationChannelGroup group : r.groups.values()) {
1539 groups.put(group.toJson());
1540 }
1541 PackagePreferences.put("groups", groups);
1542 } catch (JSONException e) {
1543 // pass
1544 }
1545 PackagePreferencess.put(PackagePreferences);
1546 }
1547 }
1548 }
1549 try {
1550 ranking.put("PackagePreferencess", PackagePreferencess);
1551 } catch (JSONException e) {
1552 // pass
1553 }
1554 return ranking;
1555 }
1556
1557 /**
1558 * Dump only the ban information as structured JSON for the stats collector.
1559 *
1560 * This is intentionally redundant with {#link dumpJson} because the old
1561 * scraper will expect this format.
1562 *
1563 * @param filter
1564 * @return
1565 */
1566 public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
1567 JSONArray bans = new JSONArray();
1568 Map<Integer, String> packageBans = getPackageBans();
1569 for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
1570 final int userId = UserHandle.getUserId(ban.getKey());
1571 final String packageName = ban.getValue();
1572 if (filter == null || filter.matches(packageName)) {
1573 JSONObject banJson = new JSONObject();
1574 try {
1575 banJson.put("userId", userId);
1576 banJson.put("packageName", packageName);
1577 } catch (JSONException e) {
1578 e.printStackTrace();
1579 }
1580 bans.put(banJson);
1581 }
1582 }
1583 return bans;
1584 }
1585
1586 public Map<Integer, String> getPackageBans() {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001587 synchronized (mPackagePreferences) {
1588 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001589 ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
1590 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001591 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001592 if (r.importance == IMPORTANCE_NONE) {
1593 packageBans.put(r.uid, r.pkg);
1594 }
1595 }
1596
1597 return packageBans;
1598 }
1599 }
1600
1601 /**
1602 * Dump only the channel information as structured JSON for the stats collector.
1603 *
1604 * This is intentionally redundant with {#link dumpJson} because the old
1605 * scraper will expect this format.
1606 *
1607 * @param filter
1608 * @return
1609 */
1610 public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
1611 JSONArray channels = new JSONArray();
1612 Map<String, Integer> packageChannels = getPackageChannels();
1613 for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) {
1614 final String packageName = channelCount.getKey();
1615 if (filter == null || filter.matches(packageName)) {
1616 JSONObject channelCountJson = new JSONObject();
1617 try {
1618 channelCountJson.put("packageName", packageName);
1619 channelCountJson.put("channelCount", channelCount.getValue());
1620 } catch (JSONException e) {
1621 e.printStackTrace();
1622 }
1623 channels.put(channelCountJson);
1624 }
1625 }
1626 return channels;
1627 }
1628
1629 private Map<String, Integer> getPackageChannels() {
1630 ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001631 synchronized (mPackagePreferences) {
1632 for (int i = 0; i < mPackagePreferences.size(); i++) {
1633 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001634 int channelCount = 0;
1635 for (int j = 0; j < r.channels.size(); j++) {
1636 if (!r.channels.valueAt(j).isDeleted()) {
1637 channelCount++;
1638 }
1639 }
1640 packageChannels.put(r.pkg, channelCount);
1641 }
1642 }
1643 return packageChannels;
1644 }
1645
Beverly0479cde22018-11-09 11:05:34 -05001646 /**
1647 * Called when user switches
1648 */
1649 public void onUserSwitched(int userId) {
1650 syncChannelsBypassingDnd(userId);
1651 }
1652
1653 /**
1654 * Called when user is unlocked
1655 */
1656 public void onUserUnlocked(int userId) {
1657 syncChannelsBypassingDnd(userId);
1658 }
1659
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001660 public void onUserRemoved(int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001661 synchronized (mPackagePreferences) {
1662 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001663 for (int i = N - 1; i >= 0; i--) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001664 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001665 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001666 mPackagePreferences.removeAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001667 }
1668 }
1669 }
1670 }
1671
1672 protected void onLocaleChanged(Context context, int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001673 synchronized (mPackagePreferences) {
1674 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001675 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001676 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001677 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
1678 if (PackagePreferences.channels.containsKey(
1679 NotificationChannel.DEFAULT_CHANNEL_ID)) {
1680 PackagePreferences.channels.get(
1681 NotificationChannel.DEFAULT_CHANNEL_ID).setName(
1682 context.getResources().getString(
1683 R.string.default_notification_channel_label));
1684 }
1685 }
1686 }
1687 }
1688 }
1689
1690 public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
1691 int[] uidList) {
1692 if (pkgList == null || pkgList.length == 0) {
1693 return; // nothing to do
1694 }
1695 boolean updated = false;
1696 if (removingPackage) {
1697 // Remove notification settings for uninstalled package
1698 int size = Math.min(pkgList.length, uidList.length);
1699 for (int i = 0; i < size; i++) {
1700 final String pkg = pkgList[i];
1701 final int uid = uidList[i];
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001702 synchronized (mPackagePreferences) {
1703 mPackagePreferences.remove(packagePreferencesKey(pkg, uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001704 }
1705 mRestoredWithoutUids.remove(pkg);
1706 updated = true;
1707 }
1708 } else {
1709 for (String pkg : pkgList) {
1710 // Package install
1711 final PackagePreferences r = mRestoredWithoutUids.get(pkg);
1712 if (r != null) {
1713 try {
1714 r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
1715 mRestoredWithoutUids.remove(pkg);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001716 synchronized (mPackagePreferences) {
1717 mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001718 }
1719 updated = true;
1720 } catch (PackageManager.NameNotFoundException e) {
1721 // noop
1722 }
1723 }
1724 // Package upgrade
1725 try {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001726 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001727 PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001728 mPm.getPackageUidAsUser(pkg, changeUserId));
1729 if (fullPackagePreferences != null) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001730 createDefaultChannelIfNeededLocked(fullPackagePreferences);
1731 deleteDefaultChannelIfNeededLocked(fullPackagePreferences);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001732 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001733 }
1734 } catch (PackageManager.NameNotFoundException e) {
1735 }
1736 }
1737 }
1738
1739 if (updated) {
1740 updateConfig();
1741 }
1742 }
1743
Julia Reynolds7af51c52019-04-19 11:08:27 -04001744 public void clearData(String pkg, int uid) {
1745 synchronized (mPackagePreferences) {
1746 PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
1747 if (p != null) {
1748 p.channels = new ArrayMap<>();
1749 p.groups = new ArrayMap<>();
1750 p.delegate = null;
1751 p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
1752 p.allowBubble = DEFAULT_ALLOW_BUBBLE;
1753 p.importance = DEFAULT_IMPORTANCE;
1754 p.priority = DEFAULT_PRIORITY;
1755 p.visibility = DEFAULT_VISIBILITY;
1756 p.showBadge = DEFAULT_SHOW_BADGE;
1757 }
1758 }
1759 }
1760
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001761 private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
1762 return new LogMaker(
1763 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1764 .ACTION_NOTIFICATION_CHANNEL)
1765 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1766 .setPackageName(pkg)
1767 .addTaggedData(
1768 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1769 .FIELD_NOTIFICATION_CHANNEL_ID,
1770 channel.getId())
1771 .addTaggedData(
1772 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1773 .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
1774 channel.getImportance());
1775 }
1776
1777 private LogMaker getChannelGroupLog(String groupId, String pkg) {
1778 return new LogMaker(
1779 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1780 .ACTION_NOTIFICATION_CHANNEL_GROUP)
1781 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1782 .addTaggedData(
1783 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1784 .FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
1785 groupId)
1786 .setPackageName(pkg);
1787 }
1788
Julia Reynolds4509ce72019-01-31 13:12:43 -05001789 public void updateBubblesEnabled() {
1790 if (mBubblesEnabled == null) {
1791 mBubblesEnabled = new SparseBooleanArray();
1792 }
1793 boolean changed = false;
1794 // update the cached values
1795 for (int index = 0; index < mBubblesEnabled.size(); index++) {
1796 int userId = mBubblesEnabled.keyAt(index);
1797 final boolean oldValue = mBubblesEnabled.get(userId);
1798 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1799 Settings.Secure.NOTIFICATION_BUBBLES,
1800 DEFAULT_ALLOW_BUBBLE ? 1 : 0, userId) != 0;
1801 mBubblesEnabled.put(userId, newValue);
1802 changed |= oldValue != newValue;
1803 }
1804 if (changed) {
1805 updateConfig();
1806 }
1807 }
1808
1809 public boolean bubblesEnabled(UserHandle userHandle) {
1810 int userId = userHandle.getIdentifier();
1811 if (userId == UserHandle.USER_ALL) {
1812 return false;
1813 }
1814 if (mBubblesEnabled.indexOfKey(userId) < 0) {
1815 mBubblesEnabled.put(userId,
1816 Settings.Secure.getIntForUser(mContext.getContentResolver(),
1817 Settings.Secure.NOTIFICATION_BUBBLES,
1818 DEFAULT_ALLOW_BUBBLE ? 1 : 0, userId) != 0);
1819 }
1820 return mBubblesEnabled.get(userId, DEFAULT_ALLOW_BUBBLE);
1821 }
1822
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001823
1824 public void updateBadgingEnabled() {
1825 if (mBadgingEnabled == null) {
1826 mBadgingEnabled = new SparseBooleanArray();
1827 }
1828 boolean changed = false;
1829 // update the cached values
1830 for (int index = 0; index < mBadgingEnabled.size(); index++) {
1831 int userId = mBadgingEnabled.keyAt(index);
1832 final boolean oldValue = mBadgingEnabled.get(userId);
1833 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1834 Settings.Secure.NOTIFICATION_BADGING,
1835 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
1836 mBadgingEnabled.put(userId, newValue);
1837 changed |= oldValue != newValue;
1838 }
1839 if (changed) {
1840 updateConfig();
1841 }
1842 }
1843
1844 public boolean badgingEnabled(UserHandle userHandle) {
1845 int userId = userHandle.getIdentifier();
1846 if (userId == UserHandle.USER_ALL) {
1847 return false;
1848 }
1849 if (mBadgingEnabled.indexOfKey(userId) < 0) {
1850 mBadgingEnabled.put(userId,
1851 Settings.Secure.getIntForUser(mContext.getContentResolver(),
1852 Settings.Secure.NOTIFICATION_BADGING,
1853 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
1854 }
1855 return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
1856 }
1857
1858 private void updateConfig() {
1859 mRankingHandler.requestSort();
1860 }
1861
1862 private static String packagePreferencesKey(String pkg, int uid) {
1863 return pkg + "|" + uid;
1864 }
1865
1866 private static class PackagePreferences {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001867 String pkg;
1868 int uid = UNKNOWN_UID;
1869 int importance = DEFAULT_IMPORTANCE;
1870 int priority = DEFAULT_PRIORITY;
1871 int visibility = DEFAULT_VISIBILITY;
1872 boolean showBadge = DEFAULT_SHOW_BADGE;
Mady Mellorc39b4ae2019-01-09 17:11:37 -08001873 boolean allowBubble = DEFAULT_ALLOW_BUBBLE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001874 int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Julia Reynolds0c245002019-03-27 16:10:11 -04001875 // these fields are loaded on boot from a different source of truth and so are not
1876 // written to notification policy xml
Julia Reynolds413ba842019-01-11 10:38:08 -05001877 boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
1878 List<String> futureOemLockedChannels = new ArrayList<>();
Julia Reynolds0c245002019-03-27 16:10:11 -04001879 boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001880
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001881 Delegate delegate = null;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001882 ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
1883 Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001884
1885 public boolean isValidDelegate(String pkg, int uid) {
1886 return delegate != null && delegate.isAllowed(pkg, uid);
1887 }
1888 }
1889
1890 private static class Delegate {
1891 static final boolean DEFAULT_ENABLED = true;
1892 static final boolean DEFAULT_USER_ALLOWED = true;
1893 String mPkg;
1894 int mUid = UNKNOWN_UID;
1895 boolean mEnabled = DEFAULT_ENABLED;
1896 boolean mUserAllowed = DEFAULT_USER_ALLOWED;
1897
1898 Delegate(String pkg, int uid, boolean enabled, boolean userAllowed) {
1899 mPkg = pkg;
1900 mUid = uid;
1901 mEnabled = enabled;
1902 mUserAllowed = userAllowed;
1903 }
1904
1905 public boolean isAllowed(String pkg, int uid) {
1906 if (pkg == null || uid == UNKNOWN_UID) {
1907 return false;
1908 }
1909 return pkg.equals(mPkg)
1910 && uid == mUid
1911 && (mUserAllowed && mEnabled);
1912 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001913 }
1914}