blob: 4835c97f0bb7f043fd7812680741fed9db712c50 [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
Julia Reynoldsb8bec9e2019-09-04 12:17:18 -040076 static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 50000;
Julia Reynolds198282a2019-08-20 16:08:42 -040077
78 @VisibleForTesting
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040079 static final String TAG_RANKING = "ranking";
80 private static final String TAG_PACKAGE = "package";
81 private static final String TAG_CHANNEL = "channel";
82 private static final String TAG_GROUP = "channelGroup";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040083 private static final String TAG_DELEGATE = "delegate";
Julia Reynolds905df642019-05-31 15:29:59 -040084 private static final String TAG_STATUS_ICONS = "silent_status_icons";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040085
86 private static final String ATT_VERSION = "version";
87 private static final String ATT_NAME = "name";
88 private static final String ATT_UID = "uid";
89 private static final String ATT_ID = "id";
Mady Mellorc39b4ae2019-01-09 17:11:37 -080090 private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040091 private static final String ATT_PRIORITY = "priority";
92 private static final String ATT_VISIBILITY = "visibility";
93 private static final String ATT_IMPORTANCE = "importance";
94 private static final String ATT_SHOW_BADGE = "show_badge";
95 private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040096 private static final String ATT_ENABLED = "enabled";
97 private static final String ATT_USER_ALLOWED = "allowed";
Julia Reynolds25692c42019-05-03 15:01:24 -040098 private static final String ATT_HIDE_SILENT = "hide_gentle";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040099
100 private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
101 private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
102 private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500103 @VisibleForTesting
Julia Reynolds25692c42019-05-03 15:01:24 -0400104 static final boolean DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS = false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400105 private static final boolean DEFAULT_SHOW_BADGE = true;
Mady Melloread1b802019-07-22 16:41:19 -0700106 static final boolean DEFAULT_ALLOW_BUBBLE = true;
Julia Reynolds413ba842019-01-11 10:38:08 -0500107 private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false;
Julia Reynolds0c245002019-03-27 16:10:11 -0400108 private static final boolean DEFAULT_APP_LOCKED_IMPORTANCE = false;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800109
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400110 /**
111 * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
112 * fields.
113 */
114 private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
115
116 /**
117 * All user-lockable fields for a given application.
118 */
119 @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
120 public @interface LockableAppFields {
121 int USER_LOCKED_IMPORTANCE = 0x00000001;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800122 int USER_LOCKED_BUBBLE = 0x00000002;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400123 }
124
125 // pkg|uid => PackagePreferences
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400126 private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400127 // pkg => PackagePreferences
128 private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
129
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400130 private final Context mContext;
131 private final PackageManager mPm;
132 private final RankingHandler mRankingHandler;
133 private final ZenModeHelper mZenModeHelper;
134
135 private SparseBooleanArray mBadgingEnabled;
Mady Mellor8e103942019-08-30 15:48:46 -0700136 private boolean mBubblesEnabled = DEFAULT_ALLOW_BUBBLE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400137 private boolean mAreChannelsBypassingDnd;
Julia Reynolds2594b472019-04-03 13:30:16 -0400138 private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400139
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400140 public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
141 ZenModeHelper zenHelper) {
142 mContext = context;
143 mZenModeHelper = zenHelper;
144 mRankingHandler = rankingHandler;
145 mPm = pm;
146
147 updateBadgingEnabled();
Julia Reynolds4509ce72019-01-31 13:12:43 -0500148 updateBubblesEnabled();
Beverly0479cde22018-11-09 11:05:34 -0500149 syncChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400150 }
151
Annie Meng8b646fd2019-02-01 18:46:42 +0000152 public void readXml(XmlPullParser parser, boolean forRestore, int userId)
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400153 throws XmlPullParserException, IOException {
154 int type = parser.getEventType();
155 if (type != XmlPullParser.START_TAG) return;
156 String tag = parser.getName();
157 if (!TAG_RANKING.equals(tag)) return;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400158 synchronized (mPackagePreferences) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500159 // Clobber groups and channels with the xml, but don't delete other data that wasn't
160 // present at the time of serialization.
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400161 mRestoredWithoutUids.clear();
162 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
163 tag = parser.getName();
164 if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
165 return;
166 }
167 if (type == XmlPullParser.START_TAG) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500168 if (TAG_STATUS_ICONS.equals(tag)) {
Annie Meng8b646fd2019-02-01 18:46:42 +0000169 if (forRestore && userId != UserHandle.USER_SYSTEM) {
170 continue;
171 }
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500172 mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute(
173 parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
174 } else if (TAG_PACKAGE.equals(tag)) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400175 int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
176 String name = parser.getAttributeValue(null, ATT_NAME);
177 if (!TextUtils.isEmpty(name)) {
178 if (forRestore) {
179 try {
Annie Meng8b646fd2019-02-01 18:46:42 +0000180 uid = mPm.getPackageUidAsUser(name, userId);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400181 } catch (PackageManager.NameNotFoundException e) {
182 // noop
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400183 }
184 }
Julia Reynolds198282a2019-08-20 16:08:42 -0400185 boolean skipWarningLogged = false;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400186
Julia Reynolds5c399c62019-04-08 14:42:53 -0400187 PackagePreferences r = getOrCreatePackagePreferencesLocked(name, uid,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400188 XmlUtils.readIntAttribute(
189 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
190 XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
191 DEFAULT_PRIORITY),
192 XmlUtils.readIntAttribute(
193 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
194 XmlUtils.readBooleanAttribute(
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500195 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
196 XmlUtils.readBooleanAttribute(
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800197 parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400198 r.importance = XmlUtils.readIntAttribute(
199 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
200 r.priority = XmlUtils.readIntAttribute(
201 parser, ATT_PRIORITY, DEFAULT_PRIORITY);
202 r.visibility = XmlUtils.readIntAttribute(
203 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
204 r.showBadge = XmlUtils.readBooleanAttribute(
205 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
206 r.lockedAppFields = XmlUtils.readIntAttribute(parser,
207 ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
208
209 final int innerDepth = parser.getDepth();
210 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
211 && (type != XmlPullParser.END_TAG
212 || parser.getDepth() > innerDepth)) {
213 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
214 continue;
215 }
216
217 String tagName = parser.getName();
218 // Channel groups
219 if (TAG_GROUP.equals(tagName)) {
220 String id = parser.getAttributeValue(null, ATT_ID);
221 CharSequence groupName = parser.getAttributeValue(null,
222 ATT_NAME);
223 if (!TextUtils.isEmpty(id)) {
224 NotificationChannelGroup group
225 = new NotificationChannelGroup(id, groupName);
226 group.populateFromXml(parser);
227 r.groups.put(id, group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400228 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400229 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400230 // Channels
231 if (TAG_CHANNEL.equals(tagName)) {
Julia Reynolds198282a2019-08-20 16:08:42 -0400232 if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
233 if (!skipWarningLogged) {
234 Slog.w(TAG, "Skipping further channels for " + r.pkg
235 + "; app has too many");
236 skipWarningLogged = true;
237 }
238 continue;
239 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400240 String id = parser.getAttributeValue(null, ATT_ID);
241 String channelName = parser.getAttributeValue(null, ATT_NAME);
242 int channelImportance = XmlUtils.readIntAttribute(
243 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
244 if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
245 NotificationChannel channel = new NotificationChannel(id,
246 channelName, channelImportance);
247 if (forRestore) {
248 channel.populateFromXmlForRestore(parser, mContext);
249 } else {
250 channel.populateFromXml(parser);
251 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -0400252 channel.setImportanceLockedByCriticalDeviceFunction(
253 r.defaultAppLockedImportance);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400254 r.channels.put(id, channel);
255 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400256 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400257 // Delegate
258 if (TAG_DELEGATE.equals(tagName)) {
259 int delegateId =
260 XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
261 String delegateName =
262 XmlUtils.readStringAttribute(parser, ATT_NAME);
263 boolean delegateEnabled = XmlUtils.readBooleanAttribute(
264 parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
265 boolean userAllowed = XmlUtils.readBooleanAttribute(
266 parser, ATT_USER_ALLOWED,
267 Delegate.DEFAULT_USER_ALLOWED);
268 Delegate d = null;
269 if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(
270 delegateName)) {
271 d = new Delegate(
272 delegateName, delegateId, delegateEnabled,
273 userAllowed);
274 }
275 r.delegate = d;
276 }
277
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400278 }
279
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400280 try {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400281 deleteDefaultChannelIfNeededLocked(r);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400282 } catch (PackageManager.NameNotFoundException e) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400283 Slog.e(TAG, "deleteDefaultChannelIfNeededLocked - Exception: " + e);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400284 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400285 }
286 }
287 }
288 }
289 }
290 throw new IllegalStateException("Failed to reach END_DOCUMENT");
291 }
292
Julia Reynolds5c399c62019-04-08 14:42:53 -0400293 private PackagePreferences getPackagePreferencesLocked(String pkg, int uid) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400294 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400295 return mPackagePreferences.get(key);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400296 }
297
Julia Reynolds5c399c62019-04-08 14:42:53 -0400298 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, int uid) {
299 return getOrCreatePackagePreferencesLocked(pkg, uid,
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500300 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800301 DEFAULT_ALLOW_BUBBLE);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400302 }
303
Julia Reynolds5c399c62019-04-08 14:42:53 -0400304 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, int uid,
305 int importance, int priority, int visibility, boolean showBadge, boolean allowBubble) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400306 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400307 PackagePreferences
308 r = (uid == UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
309 : mPackagePreferences.get(key);
310 if (r == null) {
311 r = new PackagePreferences();
312 r.pkg = pkg;
313 r.uid = uid;
314 r.importance = importance;
315 r.priority = priority;
316 r.visibility = visibility;
317 r.showBadge = showBadge;
318 r.allowBubble = allowBubble;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400319
Julia Reynolds5c399c62019-04-08 14:42:53 -0400320 try {
321 createDefaultChannelIfNeededLocked(r);
322 } catch (PackageManager.NameNotFoundException e) {
323 Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400324 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400325
326 if (r.uid == UNKNOWN_UID) {
327 mRestoredWithoutUids.put(pkg, r);
328 } else {
329 mPackagePreferences.put(key, r);
330 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400331 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400332 return r;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400333 }
334
335 private boolean shouldHaveDefaultChannel(PackagePreferences r) throws
336 PackageManager.NameNotFoundException {
337 final int userId = UserHandle.getUserId(r.uid);
338 final ApplicationInfo applicationInfo =
339 mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
340 if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
341 // O apps should not have the default channel.
342 return false;
343 }
344
345 // Otherwise, this app should have the default channel.
346 return true;
347 }
348
Julia Reynolds996c7c12019-05-24 10:25:33 -0400349 private boolean deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400350 PackageManager.NameNotFoundException {
351 if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
352 // Not present
Julia Reynolds996c7c12019-05-24 10:25:33 -0400353 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400354 }
355
356 if (shouldHaveDefaultChannel(r)) {
357 // Keep the default channel until upgraded.
Julia Reynolds996c7c12019-05-24 10:25:33 -0400358 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400359 }
360
361 // Remove Default Channel.
362 r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
Julia Reynolds996c7c12019-05-24 10:25:33 -0400363
364 return true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400365 }
366
Julia Reynolds996c7c12019-05-24 10:25:33 -0400367 private boolean createDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400368 PackageManager.NameNotFoundException {
369 if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
370 r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
371 com.android.internal.R.string.default_notification_channel_label));
Julia Reynolds996c7c12019-05-24 10:25:33 -0400372 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400373 }
374
375 if (!shouldHaveDefaultChannel(r)) {
376 // Keep the default channel until upgraded.
Julia Reynolds996c7c12019-05-24 10:25:33 -0400377 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400378 }
379
380 // Create Default Channel
381 NotificationChannel channel;
382 channel = new NotificationChannel(
383 NotificationChannel.DEFAULT_CHANNEL_ID,
384 mContext.getString(R.string.default_notification_channel_label),
385 r.importance);
386 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
387 channel.setLockscreenVisibility(r.visibility);
388 if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
389 channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
390 }
391 if (r.priority != DEFAULT_PRIORITY) {
392 channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
393 }
394 if (r.visibility != DEFAULT_VISIBILITY) {
395 channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
396 }
397 r.channels.put(channel.getId(), channel);
Julia Reynolds996c7c12019-05-24 10:25:33 -0400398
399 return true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400400 }
401
Annie Meng8b646fd2019-02-01 18:46:42 +0000402 public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400403 out.startTag(null, TAG_RANKING);
404 out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
Annie Meng8b646fd2019-02-01 18:46:42 +0000405 if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS
406 && (!forBackup || userId == UserHandle.USER_SYSTEM)) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500407 out.startTag(null, TAG_STATUS_ICONS);
408 out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons));
409 out.endTag(null, TAG_STATUS_ICONS);
410 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400411
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400412 synchronized (mPackagePreferences) {
413 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400414 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400415 final PackagePreferences r = mPackagePreferences.valueAt(i);
Annie Meng8b646fd2019-02-01 18:46:42 +0000416 if (forBackup && UserHandle.getUserId(r.uid) != userId) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400417 continue;
418 }
419 final boolean hasNonDefaultSettings =
420 r.importance != DEFAULT_IMPORTANCE
421 || r.priority != DEFAULT_PRIORITY
422 || r.visibility != DEFAULT_VISIBILITY
423 || r.showBadge != DEFAULT_SHOW_BADGE
424 || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
425 || r.channels.size() > 0
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400426 || r.groups.size() > 0
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500427 || r.delegate != null
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800428 || r.allowBubble != DEFAULT_ALLOW_BUBBLE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400429 if (hasNonDefaultSettings) {
430 out.startTag(null, TAG_PACKAGE);
431 out.attribute(null, ATT_NAME, r.pkg);
432 if (r.importance != DEFAULT_IMPORTANCE) {
433 out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
434 }
435 if (r.priority != DEFAULT_PRIORITY) {
436 out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
437 }
438 if (r.visibility != DEFAULT_VISIBILITY) {
439 out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
440 }
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800441 if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) {
442 out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble));
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500443 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400444 out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
445 out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
446 Integer.toString(r.lockedAppFields));
447
448 if (!forBackup) {
449 out.attribute(null, ATT_UID, Integer.toString(r.uid));
450 }
451
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400452 if (r.delegate != null) {
453 out.startTag(null, TAG_DELEGATE);
454
455 out.attribute(null, ATT_NAME, r.delegate.mPkg);
456 out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
457 if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
458 out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
459 }
460 if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
461 out.attribute(null, ATT_USER_ALLOWED,
462 Boolean.toString(r.delegate.mUserAllowed));
463 }
464 out.endTag(null, TAG_DELEGATE);
465 }
466
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400467 for (NotificationChannelGroup group : r.groups.values()) {
468 group.writeXml(out);
469 }
470
471 for (NotificationChannel channel : r.channels.values()) {
472 if (forBackup) {
473 if (!channel.isDeleted()) {
474 channel.writeXmlForBackup(out, mContext);
475 }
476 } else {
477 channel.writeXml(out);
478 }
479 }
480
481 out.endTag(null, TAG_PACKAGE);
482 }
483 }
484 }
485 out.endTag(null, TAG_RANKING);
486 }
487
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800488 /**
489 * Sets whether bubbles are allowed.
490 *
491 * @param pkg the package to allow or not allow bubbles for.
492 * @param uid the uid to allow or not allow bubbles for.
493 * @param allowed whether bubbles are allowed.
494 */
495 public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
Mady Mellor9f296142019-05-24 09:42:52 -0700496 boolean changed = false;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400497 synchronized (mPackagePreferences) {
498 PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid);
Mady Mellor9f296142019-05-24 09:42:52 -0700499 changed = p.allowBubble != allowed;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400500 p.allowBubble = allowed;
501 p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
502 }
Mady Mellor9f296142019-05-24 09:42:52 -0700503 if (changed) {
504 updateConfig();
505 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500506 }
507
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800508 /**
509 * Whether bubbles are allowed.
510 *
511 * @param pkg the package to check if bubbles are allowed for
512 * @param uid the uid to check if bubbles are allowed for.
513 * @return whether bubbles are allowed.
514 */
Julia Reynolds4509ce72019-01-31 13:12:43 -0500515 @Override
Mady Mellor9db685a2019-01-23 13:23:37 -0800516 public boolean areBubblesAllowed(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400517 synchronized (mPackagePreferences) {
518 return getOrCreatePackagePreferencesLocked(pkg, uid).allowBubble;
519 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500520 }
521
522 public int getAppLockedFields(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400523 synchronized (mPackagePreferences) {
524 return getOrCreatePackagePreferencesLocked(pkg, uid).lockedAppFields;
525 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500526 }
527
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400528 /**
529 * Gets importance.
530 */
531 @Override
532 public int getImportance(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400533 synchronized (mPackagePreferences) {
534 return getOrCreatePackagePreferencesLocked(packageName, uid).importance;
535 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400536 }
537
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400538 /**
539 * Returns whether the importance of the corresponding notification is user-locked and shouldn't
540 * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
541 * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
542 */
543 public boolean getIsAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400544 synchronized (mPackagePreferences) {
545 int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields;
546 return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
547 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400548 }
549
550 @Override
551 public boolean canShowBadge(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400552 synchronized (mPackagePreferences) {
553 return getOrCreatePackagePreferencesLocked(packageName, uid).showBadge;
554 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400555 }
556
557 @Override
558 public void setShowBadge(String packageName, int uid, boolean showBadge) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400559 synchronized (mPackagePreferences) {
560 getOrCreatePackagePreferencesLocked(packageName, uid).showBadge = showBadge;
561 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400562 updateConfig();
563 }
564
565 @Override
566 public boolean isGroupBlocked(String packageName, int uid, String groupId) {
567 if (groupId == null) {
568 return false;
569 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400570 synchronized (mPackagePreferences) {
571 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
572 NotificationChannelGroup group = r.groups.get(groupId);
573 if (group == null) {
574 return false;
575 }
576 return group.isBlocked();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400577 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400578 }
579
580 int getPackagePriority(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400581 synchronized (mPackagePreferences) {
582 return getOrCreatePackagePreferencesLocked(pkg, uid).priority;
583 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400584 }
585
586 int getPackageVisibility(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400587 synchronized (mPackagePreferences) {
588 return getOrCreatePackagePreferencesLocked(pkg, uid).visibility;
589 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400590 }
591
592 @Override
593 public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
594 boolean fromTargetApp) {
595 Preconditions.checkNotNull(pkg);
596 Preconditions.checkNotNull(group);
597 Preconditions.checkNotNull(group.getId());
598 Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
Julia Reynolds5c399c62019-04-08 14:42:53 -0400599 synchronized (mPackagePreferences) {
600 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
601 if (r == null) {
602 throw new IllegalArgumentException("Invalid package");
603 }
604 final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
605 if (!group.equals(oldGroup)) {
606 // will log for new entries as well as name/description changes
607 MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
608 }
609 if (oldGroup != null) {
610 group.setChannels(oldGroup.getChannels());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400611
Julia Reynolds5c399c62019-04-08 14:42:53 -0400612 // apps can't update the blocked status or app overlay permission
613 if (fromTargetApp) {
614 group.setBlocked(oldGroup.isBlocked());
615 group.unlockFields(group.getUserLockedFields());
616 group.lockFields(oldGroup.getUserLockedFields());
617 } else {
618 // but the system can
619 if (group.isBlocked() != oldGroup.isBlocked()) {
620 group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
621 updateChannelsBypassingDnd(mContext.getUserId());
622 }
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -0400623 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400624 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400625 r.groups.put(group.getId(), group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400626 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400627 }
628
629 @Override
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400630 public boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400631 boolean fromTargetApp, boolean hasDndAccess) {
632 Preconditions.checkNotNull(pkg);
633 Preconditions.checkNotNull(channel);
634 Preconditions.checkNotNull(channel.getId());
635 Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400636 boolean needsPolicyFileChange = false;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400637 synchronized (mPackagePreferences) {
638 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
639 if (r == null) {
640 throw new IllegalArgumentException("Invalid package");
641 }
642 if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
643 throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
644 }
645 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
646 throw new IllegalArgumentException("Reserved id");
647 }
648 NotificationChannel existing = r.channels.get(channel.getId());
649 // Keep most of the existing settings
650 if (existing != null && fromTargetApp) {
651 if (existing.isDeleted()) {
652 existing.setDeleted(false);
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400653 needsPolicyFileChange = true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400654
Julia Reynolds5c399c62019-04-08 14:42:53 -0400655 // log a resurrected channel as if it's new again
656 MetricsLogger.action(getChannelLog(channel, pkg).setType(
657 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
658 }
659
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400660 if (!Objects.equals(channel.getName().toString(), existing.getName().toString())) {
661 existing.setName(channel.getName().toString());
662 needsPolicyFileChange = true;
663 }
664 if (!Objects.equals(channel.getDescription(), existing.getDescription())) {
665 existing.setDescription(channel.getDescription());
666 needsPolicyFileChange = true;
667 }
668 if (channel.isBlockableSystem() != existing.isBlockableSystem()) {
669 existing.setBlockableSystem(channel.isBlockableSystem());
670 needsPolicyFileChange = true;
671 }
672 if (channel.getGroup() != null && existing.getGroup() == null) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400673 existing.setGroup(channel.getGroup());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400674 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400675 }
676
677 // Apps are allowed to downgrade channel importance if the user has not changed any
678 // fields on this channel yet.
679 final int previousExistingImportance = existing.getImportance();
680 if (existing.getUserLockedFields() == 0 &&
681 channel.getImportance() < existing.getImportance()) {
682 existing.setImportance(channel.getImportance());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400683 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400684 }
685
686 // system apps and dnd access apps can bypass dnd if the user hasn't changed any
687 // fields on the channel yet
688 if (existing.getUserLockedFields() == 0 && hasDndAccess) {
689 boolean bypassDnd = channel.canBypassDnd();
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400690 if (bypassDnd != existing.canBypassDnd()) {
691 existing.setBypassDnd(bypassDnd);
692 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400693
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400694 if (bypassDnd != mAreChannelsBypassingDnd
695 || previousExistingImportance != existing.getImportance()) {
696 updateChannelsBypassingDnd(mContext.getUserId());
697 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400698 }
699 }
700
701 updateConfig();
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400702 return needsPolicyFileChange;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400703 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400704
Julia Reynolds198282a2019-08-20 16:08:42 -0400705 if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
706 throw new IllegalStateException("Limit exceed; cannot create more channels");
707 }
708
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400709 needsPolicyFileChange = true;
710
Julia Reynolds5c399c62019-04-08 14:42:53 -0400711 if (channel.getImportance() < IMPORTANCE_NONE
712 || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
713 throw new IllegalArgumentException("Invalid importance level");
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400714 }
715
Julia Reynolds5c399c62019-04-08 14:42:53 -0400716 // Reset fields that apps aren't allowed to set.
717 if (fromTargetApp && !hasDndAccess) {
718 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400719 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400720 if (fromTargetApp) {
721 channel.setLockscreenVisibility(r.visibility);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400722 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400723 clearLockedFieldsLocked(channel);
724 channel.setImportanceLockedByOEM(r.oemLockedImportance);
725 if (!channel.isImportanceLockedByOEM()) {
726 if (r.futureOemLockedChannels.remove(channel.getId())) {
727 channel.setImportanceLockedByOEM(true);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400728 }
729 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400730 channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
731 if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
732 channel.setLockscreenVisibility(
733 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
Julia Reynolds413ba842019-01-11 10:38:08 -0500734 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400735 if (!r.showBadge) {
736 channel.setShowBadge(false);
737 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400738
Julia Reynolds5c399c62019-04-08 14:42:53 -0400739 r.channels.put(channel.getId(), channel);
740 if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
741 updateChannelsBypassingDnd(mContext.getUserId());
742 }
743 MetricsLogger.action(getChannelLog(channel, pkg).setType(
744 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400745 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400746
747 return needsPolicyFileChange;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400748 }
749
Julia Reynolds5c399c62019-04-08 14:42:53 -0400750 void clearLockedFieldsLocked(NotificationChannel channel) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400751 channel.unlockFields(channel.getUserLockedFields());
752 }
753
754 @Override
755 public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
756 boolean fromUser) {
757 Preconditions.checkNotNull(updatedChannel);
758 Preconditions.checkNotNull(updatedChannel.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400759 synchronized (mPackagePreferences) {
760 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
761 if (r == null) {
762 throw new IllegalArgumentException("Invalid package");
763 }
764 NotificationChannel channel = r.channels.get(updatedChannel.getId());
765 if (channel == null || channel.isDeleted()) {
766 throw new IllegalArgumentException("Channel does not exist");
767 }
768 if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
769 updatedChannel.setLockscreenVisibility(
770 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
771 }
772 if (fromUser) {
773 updatedChannel.lockFields(channel.getUserLockedFields());
774 lockFieldsForUpdateLocked(channel, updatedChannel);
775 } else {
776 updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
777 }
778 // no importance updates are allowed if OEM blocked it
779 updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
780 if (updatedChannel.isImportanceLockedByOEM()) {
781 updatedChannel.setImportance(channel.getImportance());
782 }
783 updatedChannel.setImportanceLockedByCriticalDeviceFunction(
784 r.defaultAppLockedImportance);
Beverly47679222019-05-16 15:46:11 -0400785 if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
786 && updatedChannel.getImportance() == IMPORTANCE_NONE) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400787 updatedChannel.setImportance(channel.getImportance());
788 }
Julia Reynolds413ba842019-01-11 10:38:08 -0500789
Julia Reynolds5c399c62019-04-08 14:42:53 -0400790 r.channels.put(updatedChannel.getId(), updatedChannel);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400791
Julia Reynolds5c399c62019-04-08 14:42:53 -0400792 if (onlyHasDefaultChannel(pkg, uid)) {
793 // copy settings to app level so they are inherited by new channels
794 // when the app migrates
795 r.importance = updatedChannel.getImportance();
796 r.priority = updatedChannel.canBypassDnd()
797 ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
798 r.visibility = updatedChannel.getLockscreenVisibility();
799 r.showBadge = updatedChannel.canShowBadge();
800 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400801
Julia Reynolds5c399c62019-04-08 14:42:53 -0400802 if (!channel.equals(updatedChannel)) {
803 // only log if there are real changes
804 MetricsLogger.action(getChannelLog(updatedChannel, pkg)
805 .setSubtype(fromUser ? 1 : 0));
806 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400807
Julia Reynolds5c399c62019-04-08 14:42:53 -0400808 if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
809 || channel.getImportance() != updatedChannel.getImportance()) {
810 updateChannelsBypassingDnd(mContext.getUserId());
811 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400812 }
813 updateConfig();
814 }
815
816 @Override
817 public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
818 boolean includeDeleted) {
819 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400820 synchronized (mPackagePreferences) {
821 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
822 if (r == null) {
823 return null;
824 }
825 if (channelId == null) {
826 channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
827 }
828 final NotificationChannel nc = r.channels.get(channelId);
829 if (nc != null && (includeDeleted || !nc.isDeleted())) {
830 return nc;
831 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400832 return null;
833 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400834 }
835
836 @Override
837 public void deleteNotificationChannel(String pkg, int uid, String channelId) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400838 synchronized (mPackagePreferences) {
839 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
840 if (r == null) {
841 return;
842 }
843 NotificationChannel channel = r.channels.get(channelId);
844 if (channel != null) {
845 channel.setDeleted(true);
846 LogMaker lm = getChannelLog(channel, pkg);
847 lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
848 MetricsLogger.action(lm);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400849
Julia Reynolds5c399c62019-04-08 14:42:53 -0400850 if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
851 updateChannelsBypassingDnd(mContext.getUserId());
852 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400853 }
854 }
855 }
856
857 @Override
858 @VisibleForTesting
859 public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
860 Preconditions.checkNotNull(pkg);
861 Preconditions.checkNotNull(channelId);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400862 synchronized (mPackagePreferences) {
863 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
864 if (r == null) {
865 return;
866 }
867 r.channels.remove(channelId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400868 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400869 }
870
871 @Override
872 public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
873 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400874 synchronized (mPackagePreferences) {
875 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
876 if (r == null) {
877 return;
878 }
879 int N = r.channels.size() - 1;
880 for (int i = N; i >= 0; i--) {
881 String key = r.channels.keyAt(i);
882 if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
883 r.channels.remove(key);
884 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400885 }
886 }
887 }
888
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500889 public boolean shouldHideSilentStatusIcons() {
890 return mHideSilentStatusBarIcons;
891 }
892
893 public void setHideSilentStatusIcons(boolean hide) {
894 mHideSilentStatusBarIcons = hide;
895 }
896
Julia Reynolds413ba842019-01-11 10:38:08 -0500897 public void lockChannelsForOEM(String[] appOrChannelList) {
898 if (appOrChannelList == null) {
899 return;
900 }
901 for (String appOrChannel : appOrChannelList) {
902 if (!TextUtils.isEmpty(appOrChannel)) {
903 String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
904 if (appSplit != null && appSplit.length > 0) {
905 String appName = appSplit[0];
906 String channelId = appSplit.length == 2 ? appSplit[1] : null;
907
908 synchronized (mPackagePreferences) {
909 for (PackagePreferences r : mPackagePreferences.values()) {
910 if (r.pkg.equals(appName)) {
911 if (channelId == null) {
912 // lock all channels for the app
913 r.oemLockedImportance = true;
914 for (NotificationChannel channel : r.channels.values()) {
915 channel.setImportanceLockedByOEM(true);
916 }
917 } else {
918 NotificationChannel channel = r.channels.get(channelId);
919 if (channel != null) {
920 channel.setImportanceLockedByOEM(true);
921 } else {
922 // if this channel shows up in the future, make sure it'll
923 // be locked immediately
924 r.futureOemLockedChannels.add(channelId);
925 }
926 }
927 }
928 }
929 }
930 }
931 }
932 }
933 }
934
Julia Reynoldse7ca31b2019-04-25 15:41:47 -0400935 public void updateDefaultApps(int userId, ArraySet<String> toRemove,
936 ArraySet<Pair<String, Integer>> toAdd) {
Julia Reynolds0c245002019-03-27 16:10:11 -0400937 synchronized (mPackagePreferences) {
938 for (PackagePreferences p : mPackagePreferences.values()) {
939 if (userId == UserHandle.getUserId(p.uid)) {
940 if (toRemove != null && toRemove.contains(p.pkg)) {
941 p.defaultAppLockedImportance = false;
942 for (NotificationChannel channel : p.channels.values()) {
943 channel.setImportanceLockedByCriticalDeviceFunction(false);
944 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -0400945 }
946 }
947 }
948 if (toAdd != null) {
949 for (Pair<String, Integer> approvedApp : toAdd) {
950 PackagePreferences p = getOrCreatePackagePreferencesLocked(approvedApp.first,
951 approvedApp.second);
952 p.defaultAppLockedImportance = true;
953 for (NotificationChannel channel : p.channels.values()) {
954 channel.setImportanceLockedByCriticalDeviceFunction(true);
Julia Reynolds0c245002019-03-27 16:10:11 -0400955 }
956 }
957 }
958 }
959 }
960
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400961 public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
962 int uid, String groupId, boolean includeDeleted) {
963 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400964 synchronized (mPackagePreferences) {
965 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
966 if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
967 return null;
968 }
969 NotificationChannelGroup group = r.groups.get(groupId).clone();
970 group.setChannels(new ArrayList<>());
971 int N = r.channels.size();
972 for (int i = 0; i < N; i++) {
973 final NotificationChannel nc = r.channels.valueAt(i);
974 if (includeDeleted || !nc.isDeleted()) {
975 if (groupId.equals(nc.getGroup())) {
976 group.addChannel(nc);
977 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400978 }
979 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400980 return group;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400981 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400982 }
983
984 public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
985 int uid) {
986 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400987 synchronized (mPackagePreferences) {
988 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
989 if (r == null) {
990 return null;
991 }
992 return r.groups.get(groupId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400993 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400994 }
995
996 @Override
997 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
Julia Reynolds13ed28b2018-09-21 15:20:13 -0400998 int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400999 Preconditions.checkNotNull(pkg);
1000 Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001001 synchronized (mPackagePreferences) {
1002 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1003 if (r == null) {
1004 return ParceledListSlice.emptyList();
1005 }
1006 NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
1007 int N = r.channels.size();
1008 for (int i = 0; i < N; i++) {
1009 final NotificationChannel nc = r.channels.valueAt(i);
1010 if (includeDeleted || !nc.isDeleted()) {
1011 if (nc.getGroup() != null) {
1012 if (r.groups.get(nc.getGroup()) != null) {
1013 NotificationChannelGroup ncg = groups.get(nc.getGroup());
1014 if (ncg == null) {
1015 ncg = r.groups.get(nc.getGroup()).clone();
1016 ncg.setChannels(new ArrayList<>());
1017 groups.put(nc.getGroup(), ncg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001018
Julia Reynolds5c399c62019-04-08 14:42:53 -04001019 }
1020 ncg.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001021 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001022 } else {
1023 nonGrouped.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001024 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001025 }
1026 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001027 if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
1028 groups.put(null, nonGrouped);
1029 }
1030 if (includeEmpty) {
1031 for (NotificationChannelGroup group : r.groups.values()) {
1032 if (!groups.containsKey(group.getId())) {
1033 groups.put(group.getId(), group);
1034 }
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001035 }
1036 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001037 return new ParceledListSlice<>(new ArrayList<>(groups.values()));
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001038 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001039 }
1040
1041 public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
1042 String groupId) {
1043 List<NotificationChannel> deletedChannels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001044 synchronized (mPackagePreferences) {
1045 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1046 if (r == null || TextUtils.isEmpty(groupId)) {
1047 return deletedChannels;
1048 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001049
Julia Reynolds5c399c62019-04-08 14:42:53 -04001050 r.groups.remove(groupId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001051
Julia Reynolds5c399c62019-04-08 14:42:53 -04001052 int N = r.channels.size();
1053 for (int i = 0; i < N; i++) {
1054 final NotificationChannel nc = r.channels.valueAt(i);
1055 if (groupId.equals(nc.getGroup())) {
1056 nc.setDeleted(true);
1057 deletedChannels.add(nc);
1058 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001059 }
1060 }
1061 return deletedChannels;
1062 }
1063
1064 @Override
1065 public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
1066 int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001067 List<NotificationChannelGroup> groups = new ArrayList<>();
1068 synchronized (mPackagePreferences) {
1069 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1070 if (r == null) {
1071 return groups;
1072 }
1073 groups.addAll(r.groups.values());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001074 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001075 return groups;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001076 }
1077
1078 @Override
1079 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
1080 boolean includeDeleted) {
1081 Preconditions.checkNotNull(pkg);
1082 List<NotificationChannel> channels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001083 synchronized (mPackagePreferences) {
1084 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1085 if (r == null) {
1086 return ParceledListSlice.emptyList();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001087 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001088 int N = r.channels.size();
1089 for (int i = 0; i < N; i++) {
1090 final NotificationChannel nc = r.channels.valueAt(i);
1091 if (includeDeleted || !nc.isDeleted()) {
1092 channels.add(nc);
1093 }
1094 }
1095 return new ParceledListSlice<>(channels);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001096 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001097 }
1098
1099 /**
Beverly0479cde22018-11-09 11:05:34 -05001100 * Gets all notification channels associated with the given pkg and userId that can bypass dnd
1101 */
1102 public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
1103 int userId) {
1104 List<NotificationChannel> channels = new ArrayList<>();
1105 synchronized (mPackagePreferences) {
1106 final PackagePreferences r = mPackagePreferences.get(
1107 packagePreferencesKey(pkg, userId));
1108 // notifications from this package aren't blocked
1109 if (r != null && r.importance != IMPORTANCE_NONE) {
1110 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001111 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001112 channels.add(channel);
1113 }
1114 }
1115 }
1116 }
1117 return new ParceledListSlice<>(channels);
1118 }
1119
1120 /**
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001121 * True for pre-O apps that only have the default channel, or pre O apps that have no
1122 * channels yet. This method will create the default channel for pre-O apps that don't have it.
1123 * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
1124 * upgrades.
1125 */
1126 public boolean onlyHasDefaultChannel(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001127 synchronized (mPackagePreferences) {
1128 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1129 if (r.channels.size() == 1
1130 && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
1131 return true;
1132 }
1133 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001134 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001135 }
1136
1137 public int getDeletedChannelCount(String pkg, int uid) {
1138 Preconditions.checkNotNull(pkg);
1139 int deletedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001140 synchronized (mPackagePreferences) {
1141 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1142 if (r == null) {
1143 return deletedCount;
1144 }
1145 int N = r.channels.size();
1146 for (int i = 0; i < N; i++) {
1147 final NotificationChannel nc = r.channels.valueAt(i);
1148 if (nc.isDeleted()) {
1149 deletedCount++;
1150 }
1151 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001152 return deletedCount;
1153 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001154 }
1155
1156 public int getBlockedChannelCount(String pkg, int uid) {
1157 Preconditions.checkNotNull(pkg);
1158 int blockedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001159 synchronized (mPackagePreferences) {
1160 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1161 if (r == null) {
1162 return blockedCount;
1163 }
1164 int N = r.channels.size();
1165 for (int i = 0; i < N; i++) {
1166 final NotificationChannel nc = r.channels.valueAt(i);
1167 if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
1168 blockedCount++;
1169 }
1170 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001171 return blockedCount;
1172 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001173 }
1174
1175 public int getBlockedAppCount(int userId) {
1176 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001177 synchronized (mPackagePreferences) {
1178 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001179 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001180 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001181 if (userId == UserHandle.getUserId(r.uid)
1182 && r.importance == IMPORTANCE_NONE) {
1183 count++;
1184 }
1185 }
1186 }
1187 return count;
1188 }
1189
Beverly0479cde22018-11-09 11:05:34 -05001190 /**
1191 * Returns the number of apps that have at least one notification channel that can bypass DND
1192 * for given particular user
1193 */
1194 public int getAppsBypassingDndCount(int userId) {
1195 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001196 synchronized (mPackagePreferences) {
Beverly0479cde22018-11-09 11:05:34 -05001197 final int numPackagePreferences = mPackagePreferences.size();
1198 for (int i = 0; i < numPackagePreferences; i++) {
1199 final PackagePreferences r = mPackagePreferences.valueAt(i);
1200 // Package isn't associated with this userId or notifications from this package are
1201 // blocked
1202 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1203 continue;
1204 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001205
Beverly0479cde22018-11-09 11:05:34 -05001206 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001207 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001208 count++;
1209 break;
1210 }
1211 }
1212 }
1213 }
1214 return count;
1215 }
1216
1217 /**
1218 * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
1219 * updating
1220 * @param userId
1221 */
1222 private void syncChannelsBypassingDnd(int userId) {
1223 mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
1224 & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
1225 updateChannelsBypassingDnd(userId);
1226 }
1227
1228 /**
1229 * Updates the user's NotificationPolicy based on whether the given userId
1230 * has channels bypassing DND
1231 * @param userId
1232 */
1233 private void updateChannelsBypassingDnd(int userId) {
1234 synchronized (mPackagePreferences) {
1235 final int numPackagePreferences = mPackagePreferences.size();
1236 for (int i = 0; i < numPackagePreferences; i++) {
1237 final PackagePreferences r = mPackagePreferences.valueAt(i);
1238 // Package isn't associated with this userId or notifications from this package are
1239 // blocked
1240 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1241 continue;
1242 }
1243
1244 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001245 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001246 if (!mAreChannelsBypassingDnd) {
1247 mAreChannelsBypassingDnd = true;
1248 updateZenPolicy(true);
1249 }
1250 return;
1251 }
1252 }
1253 }
1254 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001255 // If no channels bypass DND, update the zen policy once to disable DND bypass.
1256 if (mAreChannelsBypassingDnd) {
1257 mAreChannelsBypassingDnd = false;
1258 updateZenPolicy(false);
1259 }
1260 }
1261
Julia Reynolds5c399c62019-04-08 14:42:53 -04001262 private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
Beverly0479cde22018-11-09 11:05:34 -05001263 // Channel is in a group that's blocked
Beverly4f7b53d2018-11-20 09:56:31 -05001264 if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
1265 return false;
Beverly0479cde22018-11-09 11:05:34 -05001266 }
1267
1268 // Channel is deleted or is blocked
1269 if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
1270 return false;
1271 }
1272
1273 return true;
1274 }
1275
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001276 public void updateZenPolicy(boolean areChannelsBypassingDnd) {
1277 NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
1278 mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
1279 policy.priorityCategories, policy.priorityCallSenders,
1280 policy.priorityMessageSenders, policy.suppressedVisualEffects,
1281 (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
1282 : 0)));
1283 }
1284
1285 public boolean areChannelsBypassingDnd() {
1286 return mAreChannelsBypassingDnd;
1287 }
1288
1289 /**
1290 * Sets importance.
1291 */
1292 @Override
1293 public void setImportance(String pkgName, int uid, int importance) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001294 synchronized (mPackagePreferences) {
1295 getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance;
1296 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001297 updateConfig();
1298 }
1299
1300 public void setEnabled(String packageName, int uid, boolean enabled) {
1301 boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
1302 if (wasEnabled == enabled) {
1303 return;
1304 }
1305 setImportance(packageName, uid,
1306 enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
1307 }
1308
1309 /**
1310 * Sets whether any notifications from the app, represented by the given {@code pkgName} and
1311 * {@code uid}, have their importance locked by the user. Locked notifications don't get
1312 * considered for sentiment adjustments (and thus never show a blocking helper).
1313 */
1314 public void setAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001315 synchronized (mPackagePreferences) {
1316 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(packageName, uid);
1317 if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
1318 return;
1319 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001320
Julia Reynolds5c399c62019-04-08 14:42:53 -04001321 prefs.lockedAppFields =
1322 prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
1323 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001324 updateConfig();
1325 }
1326
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001327 /**
1328 * Returns the delegate for a given package, if it's allowed by the package and the user.
1329 */
1330 public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001331 synchronized (mPackagePreferences) {
1332 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001333
Julia Reynolds5c399c62019-04-08 14:42:53 -04001334 if (prefs == null || prefs.delegate == null) {
1335 return null;
1336 }
1337 if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
1338 return null;
1339 }
1340 return prefs.delegate.mPkg;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001341 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001342 }
1343
1344 /**
1345 * Used by an app to delegate notification posting privileges to another apps.
1346 */
1347 public void setNotificationDelegate(String sourcePkg, int sourceUid,
1348 String delegatePkg, int delegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001349 synchronized (mPackagePreferences) {
1350 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001351
Julia Reynolds5c399c62019-04-08 14:42:53 -04001352 boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
1353 Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
1354 prefs.delegate = delegate;
1355 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001356 updateConfig();
1357 }
1358
1359 /**
1360 * Used by an app to turn off its notification delegate.
1361 */
1362 public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001363 boolean changed = false;
1364 synchronized (mPackagePreferences) {
1365 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1366 if (prefs != null && prefs.delegate != null) {
1367 prefs.delegate.mEnabled = false;
1368 changed = true;
1369 }
1370 }
1371 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001372 updateConfig();
1373 }
1374 }
1375
1376 /**
1377 * Toggles whether an app can have a notification delegate on behalf of a user.
1378 */
1379 public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001380 boolean changed = false;
1381 synchronized (mPackagePreferences) {
1382 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1383 if (prefs != null && prefs.delegate != null) {
1384 prefs.delegate.mUserAllowed = userAllowed;
1385 changed = true;
1386 }
1387 }
1388 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001389 updateConfig();
1390 }
1391 }
1392
1393 /**
1394 * Returns whether the given app is allowed on post notifications on behalf of the other given
1395 * app.
1396 */
1397 public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
1398 String potentialDelegatePkg, int potentialDelegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001399 synchronized (mPackagePreferences) {
1400 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001401
Julia Reynolds5c399c62019-04-08 14:42:53 -04001402 return prefs != null && prefs.isValidDelegate(potentialDelegatePkg,
1403 potentialDelegateUid);
1404 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001405 }
1406
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001407 @VisibleForTesting
Julia Reynolds5c399c62019-04-08 14:42:53 -04001408 void lockFieldsForUpdateLocked(NotificationChannel original, NotificationChannel update) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001409 if (original.canBypassDnd() != update.canBypassDnd()) {
1410 update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
1411 }
1412 if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
1413 update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
1414 }
1415 if (original.getImportance() != update.getImportance()) {
1416 update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
1417 }
1418 if (original.shouldShowLights() != update.shouldShowLights()
1419 || original.getLightColor() != update.getLightColor()) {
1420 update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
1421 }
1422 if (!Objects.equals(original.getSound(), update.getSound())) {
1423 update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
1424 }
1425 if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
1426 || original.shouldVibrate() != update.shouldVibrate()) {
1427 update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
1428 }
1429 if (original.canShowBadge() != update.canShowBadge()) {
1430 update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
1431 }
Julia Reynolds4509ce72019-01-31 13:12:43 -05001432 if (original.canBubble() != update.canBubble()) {
Mady Mellorc39b4ae2019-01-09 17:11:37 -08001433 update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE);
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -04001434 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001435 }
1436
1437 public void dump(PrintWriter pw, String prefix,
1438 @NonNull NotificationManagerService.DumpFilter filter) {
1439 pw.print(prefix);
1440 pw.println("per-package config:");
1441
Julia Reynolds5c399c62019-04-08 14:42:53 -04001442 pw.println("PackagePreferences:");
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001443 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001444 dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001445 }
1446 pw.println("Restored without uid:");
Julia Reynolds5c399c62019-04-08 14:42:53 -04001447 dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001448 }
1449
1450 public void dump(ProtoOutputStream proto,
1451 @NonNull NotificationManagerService.DumpFilter filter) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001452 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001453 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001454 mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001455 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001456 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001457 mRestoredWithoutUids);
1458 }
1459
Julia Reynolds5c399c62019-04-08 14:42:53 -04001460 private static void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001461 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001462 ArrayMap<String, PackagePreferences> packagePreferences) {
1463 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001464 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001465 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001466 if (filter.matches(r.pkg)) {
1467 pw.print(prefix);
1468 pw.print(" AppSettings: ");
1469 pw.print(r.pkg);
1470 pw.print(" (");
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001471 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001472 pw.print(')');
1473 if (r.importance != DEFAULT_IMPORTANCE) {
1474 pw.print(" importance=");
1475 pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
1476 }
1477 if (r.priority != DEFAULT_PRIORITY) {
1478 pw.print(" priority=");
1479 pw.print(Notification.priorityToString(r.priority));
1480 }
1481 if (r.visibility != DEFAULT_VISIBILITY) {
1482 pw.print(" visibility=");
1483 pw.print(Notification.visibilityToString(r.visibility));
1484 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001485 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1486 pw.print(" showBadge=");
1487 pw.print(r.showBadge);
1488 }
1489 if (r.defaultAppLockedImportance != DEFAULT_APP_LOCKED_IMPORTANCE) {
1490 pw.print(" defaultAppLocked=");
1491 pw.print(r.defaultAppLockedImportance);
1492 }
1493 if (r.oemLockedImportance != DEFAULT_OEM_LOCKED_IMPORTANCE) {
1494 pw.print(" oemLocked=");
1495 pw.print(r.oemLockedImportance);
1496 }
1497 if (!r.futureOemLockedChannels.isEmpty()) {
1498 pw.print(" futureLockedChannels=");
1499 pw.print(r.futureOemLockedChannels);
1500 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001501 pw.println();
1502 for (NotificationChannel channel : r.channels.values()) {
1503 pw.print(prefix);
1504 channel.dump(pw, " ", filter.redact);
1505 }
1506 for (NotificationChannelGroup group : r.groups.values()) {
1507 pw.print(prefix);
1508 pw.print(" ");
1509 pw.print(" ");
1510 pw.println(group);
1511 }
1512 }
1513 }
1514 }
1515
Julia Reynolds5c399c62019-04-08 14:42:53 -04001516 private static void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001517 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001518 ArrayMap<String, PackagePreferences> packagePreferences) {
1519 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001520 long fToken;
1521 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001522 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001523 if (filter.matches(r.pkg)) {
1524 fToken = proto.start(fieldId);
1525
1526 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
1527 proto.write(RankingHelperProto.RecordProto.UID, r.uid);
1528 proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
1529 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
1530 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
1531 proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
1532
1533 for (NotificationChannel channel : r.channels.values()) {
1534 channel.writeToProto(proto, RankingHelperProto.RecordProto.CHANNELS);
1535 }
1536 for (NotificationChannelGroup group : r.groups.values()) {
1537 group.writeToProto(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS);
1538 }
1539
1540 proto.end(fToken);
1541 }
1542 }
1543 }
1544
1545 public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
1546 JSONObject ranking = new JSONObject();
1547 JSONArray PackagePreferencess = new JSONArray();
1548 try {
1549 ranking.put("noUid", mRestoredWithoutUids.size());
1550 } catch (JSONException e) {
1551 // pass
1552 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001553 synchronized (mPackagePreferences) {
1554 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001555 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001556 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001557 if (filter == null || filter.matches(r.pkg)) {
1558 JSONObject PackagePreferences = new JSONObject();
1559 try {
1560 PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
1561 PackagePreferences.put("packageName", r.pkg);
1562 if (r.importance != DEFAULT_IMPORTANCE) {
1563 PackagePreferences.put("importance",
1564 NotificationListenerService.Ranking.importanceToString(
1565 r.importance));
1566 }
1567 if (r.priority != DEFAULT_PRIORITY) {
1568 PackagePreferences.put("priority",
1569 Notification.priorityToString(r.priority));
1570 }
1571 if (r.visibility != DEFAULT_VISIBILITY) {
1572 PackagePreferences.put("visibility",
1573 Notification.visibilityToString(r.visibility));
1574 }
1575 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1576 PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
1577 }
1578 JSONArray channels = new JSONArray();
1579 for (NotificationChannel channel : r.channels.values()) {
1580 channels.put(channel.toJson());
1581 }
1582 PackagePreferences.put("channels", channels);
1583 JSONArray groups = new JSONArray();
1584 for (NotificationChannelGroup group : r.groups.values()) {
1585 groups.put(group.toJson());
1586 }
1587 PackagePreferences.put("groups", groups);
1588 } catch (JSONException e) {
1589 // pass
1590 }
1591 PackagePreferencess.put(PackagePreferences);
1592 }
1593 }
1594 }
1595 try {
1596 ranking.put("PackagePreferencess", PackagePreferencess);
1597 } catch (JSONException e) {
1598 // pass
1599 }
1600 return ranking;
1601 }
1602
1603 /**
1604 * Dump only the ban information as structured JSON for the stats collector.
1605 *
1606 * This is intentionally redundant with {#link dumpJson} because the old
1607 * scraper will expect this format.
1608 *
1609 * @param filter
1610 * @return
1611 */
1612 public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
1613 JSONArray bans = new JSONArray();
1614 Map<Integer, String> packageBans = getPackageBans();
1615 for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
1616 final int userId = UserHandle.getUserId(ban.getKey());
1617 final String packageName = ban.getValue();
1618 if (filter == null || filter.matches(packageName)) {
1619 JSONObject banJson = new JSONObject();
1620 try {
1621 banJson.put("userId", userId);
1622 banJson.put("packageName", packageName);
1623 } catch (JSONException e) {
1624 e.printStackTrace();
1625 }
1626 bans.put(banJson);
1627 }
1628 }
1629 return bans;
1630 }
1631
1632 public Map<Integer, String> getPackageBans() {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001633 synchronized (mPackagePreferences) {
1634 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001635 ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
1636 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001637 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001638 if (r.importance == IMPORTANCE_NONE) {
1639 packageBans.put(r.uid, r.pkg);
1640 }
1641 }
1642
1643 return packageBans;
1644 }
1645 }
1646
1647 /**
1648 * Dump only the channel information as structured JSON for the stats collector.
1649 *
1650 * This is intentionally redundant with {#link dumpJson} because the old
1651 * scraper will expect this format.
1652 *
1653 * @param filter
1654 * @return
1655 */
1656 public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
1657 JSONArray channels = new JSONArray();
1658 Map<String, Integer> packageChannels = getPackageChannels();
1659 for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) {
1660 final String packageName = channelCount.getKey();
1661 if (filter == null || filter.matches(packageName)) {
1662 JSONObject channelCountJson = new JSONObject();
1663 try {
1664 channelCountJson.put("packageName", packageName);
1665 channelCountJson.put("channelCount", channelCount.getValue());
1666 } catch (JSONException e) {
1667 e.printStackTrace();
1668 }
1669 channels.put(channelCountJson);
1670 }
1671 }
1672 return channels;
1673 }
1674
1675 private Map<String, Integer> getPackageChannels() {
1676 ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001677 synchronized (mPackagePreferences) {
1678 for (int i = 0; i < mPackagePreferences.size(); i++) {
1679 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001680 int channelCount = 0;
1681 for (int j = 0; j < r.channels.size(); j++) {
1682 if (!r.channels.valueAt(j).isDeleted()) {
1683 channelCount++;
1684 }
1685 }
1686 packageChannels.put(r.pkg, channelCount);
1687 }
1688 }
1689 return packageChannels;
1690 }
1691
Beverly0479cde22018-11-09 11:05:34 -05001692 /**
1693 * Called when user switches
1694 */
1695 public void onUserSwitched(int userId) {
1696 syncChannelsBypassingDnd(userId);
1697 }
1698
1699 /**
1700 * Called when user is unlocked
1701 */
1702 public void onUserUnlocked(int userId) {
1703 syncChannelsBypassingDnd(userId);
1704 }
1705
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001706 public void onUserRemoved(int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001707 synchronized (mPackagePreferences) {
1708 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001709 for (int i = N - 1; i >= 0; i--) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001710 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001711 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001712 mPackagePreferences.removeAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001713 }
1714 }
1715 }
1716 }
1717
1718 protected void onLocaleChanged(Context context, int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001719 synchronized (mPackagePreferences) {
1720 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001721 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001722 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001723 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
1724 if (PackagePreferences.channels.containsKey(
1725 NotificationChannel.DEFAULT_CHANNEL_ID)) {
1726 PackagePreferences.channels.get(
1727 NotificationChannel.DEFAULT_CHANNEL_ID).setName(
1728 context.getResources().getString(
1729 R.string.default_notification_channel_label));
1730 }
1731 }
1732 }
1733 }
1734 }
1735
Julia Reynolds996c7c12019-05-24 10:25:33 -04001736 public boolean onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001737 int[] uidList) {
1738 if (pkgList == null || pkgList.length == 0) {
Julia Reynolds996c7c12019-05-24 10:25:33 -04001739 return false; // nothing to do
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001740 }
1741 boolean updated = false;
1742 if (removingPackage) {
1743 // Remove notification settings for uninstalled package
1744 int size = Math.min(pkgList.length, uidList.length);
1745 for (int i = 0; i < size; i++) {
1746 final String pkg = pkgList[i];
1747 final int uid = uidList[i];
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001748 synchronized (mPackagePreferences) {
1749 mPackagePreferences.remove(packagePreferencesKey(pkg, uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001750 }
1751 mRestoredWithoutUids.remove(pkg);
1752 updated = true;
1753 }
1754 } else {
1755 for (String pkg : pkgList) {
1756 // Package install
1757 final PackagePreferences r = mRestoredWithoutUids.get(pkg);
1758 if (r != null) {
1759 try {
1760 r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
1761 mRestoredWithoutUids.remove(pkg);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001762 synchronized (mPackagePreferences) {
1763 mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001764 }
1765 updated = true;
1766 } catch (PackageManager.NameNotFoundException e) {
1767 // noop
1768 }
1769 }
1770 // Package upgrade
1771 try {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001772 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001773 PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001774 mPm.getPackageUidAsUser(pkg, changeUserId));
1775 if (fullPackagePreferences != null) {
Julia Reynolds996c7c12019-05-24 10:25:33 -04001776 updated |= createDefaultChannelIfNeededLocked(fullPackagePreferences);
1777 updated |= deleteDefaultChannelIfNeededLocked(fullPackagePreferences);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001778 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001779 }
1780 } catch (PackageManager.NameNotFoundException e) {
1781 }
1782 }
1783 }
1784
1785 if (updated) {
1786 updateConfig();
1787 }
Julia Reynolds996c7c12019-05-24 10:25:33 -04001788 return updated;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001789 }
1790
Julia Reynolds7af51c52019-04-19 11:08:27 -04001791 public void clearData(String pkg, int uid) {
1792 synchronized (mPackagePreferences) {
1793 PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
1794 if (p != null) {
1795 p.channels = new ArrayMap<>();
1796 p.groups = new ArrayMap<>();
1797 p.delegate = null;
1798 p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
1799 p.allowBubble = DEFAULT_ALLOW_BUBBLE;
1800 p.importance = DEFAULT_IMPORTANCE;
1801 p.priority = DEFAULT_PRIORITY;
1802 p.visibility = DEFAULT_VISIBILITY;
1803 p.showBadge = DEFAULT_SHOW_BADGE;
1804 }
1805 }
1806 }
1807
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001808 private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
1809 return new LogMaker(
1810 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1811 .ACTION_NOTIFICATION_CHANNEL)
1812 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1813 .setPackageName(pkg)
1814 .addTaggedData(
1815 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1816 .FIELD_NOTIFICATION_CHANNEL_ID,
1817 channel.getId())
1818 .addTaggedData(
1819 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1820 .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
1821 channel.getImportance());
1822 }
1823
1824 private LogMaker getChannelGroupLog(String groupId, String pkg) {
1825 return new LogMaker(
1826 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1827 .ACTION_NOTIFICATION_CHANNEL_GROUP)
1828 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1829 .addTaggedData(
1830 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1831 .FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
1832 groupId)
1833 .setPackageName(pkg);
1834 }
1835
Julia Reynolds4509ce72019-01-31 13:12:43 -05001836 public void updateBubblesEnabled() {
Lyn Han4463f842019-07-09 15:27:28 -07001837 final boolean newValue = Settings.Global.getInt(mContext.getContentResolver(),
1838 Settings.Global.NOTIFICATION_BUBBLES,
1839 DEFAULT_ALLOW_BUBBLE ? 1 : 0) == 1;
1840 if (newValue != mBubblesEnabled) {
1841 mBubblesEnabled = newValue;
Julia Reynolds4509ce72019-01-31 13:12:43 -05001842 updateConfig();
1843 }
1844 }
1845
Lyn Han4463f842019-07-09 15:27:28 -07001846 public boolean bubblesEnabled() {
1847 return mBubblesEnabled;
Julia Reynolds4509ce72019-01-31 13:12:43 -05001848 }
1849
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001850 public void updateBadgingEnabled() {
1851 if (mBadgingEnabled == null) {
1852 mBadgingEnabled = new SparseBooleanArray();
1853 }
1854 boolean changed = false;
1855 // update the cached values
1856 for (int index = 0; index < mBadgingEnabled.size(); index++) {
1857 int userId = mBadgingEnabled.keyAt(index);
1858 final boolean oldValue = mBadgingEnabled.get(userId);
1859 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1860 Settings.Secure.NOTIFICATION_BADGING,
1861 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
1862 mBadgingEnabled.put(userId, newValue);
1863 changed |= oldValue != newValue;
1864 }
1865 if (changed) {
1866 updateConfig();
1867 }
1868 }
1869
1870 public boolean badgingEnabled(UserHandle userHandle) {
1871 int userId = userHandle.getIdentifier();
1872 if (userId == UserHandle.USER_ALL) {
1873 return false;
1874 }
1875 if (mBadgingEnabled.indexOfKey(userId) < 0) {
1876 mBadgingEnabled.put(userId,
1877 Settings.Secure.getIntForUser(mContext.getContentResolver(),
1878 Settings.Secure.NOTIFICATION_BADGING,
1879 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
1880 }
1881 return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
1882 }
1883
1884 private void updateConfig() {
1885 mRankingHandler.requestSort();
1886 }
1887
1888 private static String packagePreferencesKey(String pkg, int uid) {
1889 return pkg + "|" + uid;
1890 }
1891
1892 private static class PackagePreferences {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001893 String pkg;
1894 int uid = UNKNOWN_UID;
1895 int importance = DEFAULT_IMPORTANCE;
1896 int priority = DEFAULT_PRIORITY;
1897 int visibility = DEFAULT_VISIBILITY;
1898 boolean showBadge = DEFAULT_SHOW_BADGE;
Mady Mellorc39b4ae2019-01-09 17:11:37 -08001899 boolean allowBubble = DEFAULT_ALLOW_BUBBLE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001900 int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Julia Reynolds0c245002019-03-27 16:10:11 -04001901 // these fields are loaded on boot from a different source of truth and so are not
1902 // written to notification policy xml
Julia Reynolds413ba842019-01-11 10:38:08 -05001903 boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
1904 List<String> futureOemLockedChannels = new ArrayList<>();
Julia Reynolds0c245002019-03-27 16:10:11 -04001905 boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001906
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001907 Delegate delegate = null;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001908 ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
1909 Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001910
1911 public boolean isValidDelegate(String pkg, int uid) {
1912 return delegate != null && delegate.isAllowed(pkg, uid);
1913 }
1914 }
1915
1916 private static class Delegate {
1917 static final boolean DEFAULT_ENABLED = true;
1918 static final boolean DEFAULT_USER_ALLOWED = true;
1919 String mPkg;
1920 int mUid = UNKNOWN_UID;
1921 boolean mEnabled = DEFAULT_ENABLED;
1922 boolean mUserAllowed = DEFAULT_USER_ALLOWED;
1923
1924 Delegate(String pkg, int uid, boolean enabled, boolean userAllowed) {
1925 mPkg = pkg;
1926 mUid = uid;
1927 mEnabled = enabled;
1928 mUserAllowed = userAllowed;
1929 }
1930
1931 public boolean isAllowed(String pkg, int uid) {
1932 if (pkg == null || uid == UNKNOWN_UID) {
1933 return false;
1934 }
1935 return pkg.equals(mPkg)
1936 && uid == mUid
1937 && (mUserAllowed && mEnabled);
1938 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001939 }
1940}