blob: 7b45a1b1997c5955b8795f907f66f176bc85096c [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 Reynolds905df642019-05-31 15:29:59 -040081 private static final String TAG_STATUS_ICONS = "silent_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 Reynolds996c7c12019-05-24 10:25:33 -0400337 private boolean 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
Julia Reynolds996c7c12019-05-24 10:25:33 -0400341 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400342 }
343
344 if (shouldHaveDefaultChannel(r)) {
345 // Keep the default channel until upgraded.
Julia Reynolds996c7c12019-05-24 10:25:33 -0400346 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400347 }
348
349 // Remove Default Channel.
350 r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
Julia Reynolds996c7c12019-05-24 10:25:33 -0400351
352 return true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400353 }
354
Julia Reynolds996c7c12019-05-24 10:25:33 -0400355 private boolean createDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400356 PackageManager.NameNotFoundException {
357 if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
358 r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
359 com.android.internal.R.string.default_notification_channel_label));
Julia Reynolds996c7c12019-05-24 10:25:33 -0400360 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400361 }
362
363 if (!shouldHaveDefaultChannel(r)) {
364 // Keep the default channel until upgraded.
Julia Reynolds996c7c12019-05-24 10:25:33 -0400365 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400366 }
367
368 // Create Default Channel
369 NotificationChannel channel;
370 channel = new NotificationChannel(
371 NotificationChannel.DEFAULT_CHANNEL_ID,
372 mContext.getString(R.string.default_notification_channel_label),
373 r.importance);
374 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
375 channel.setLockscreenVisibility(r.visibility);
376 if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
377 channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
378 }
379 if (r.priority != DEFAULT_PRIORITY) {
380 channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
381 }
382 if (r.visibility != DEFAULT_VISIBILITY) {
383 channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
384 }
385 r.channels.put(channel.getId(), channel);
Julia Reynolds996c7c12019-05-24 10:25:33 -0400386
387 return true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400388 }
389
Annie Meng8b646fd2019-02-01 18:46:42 +0000390 public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400391 out.startTag(null, TAG_RANKING);
392 out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
Annie Meng8b646fd2019-02-01 18:46:42 +0000393 if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS
394 && (!forBackup || userId == UserHandle.USER_SYSTEM)) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500395 out.startTag(null, TAG_STATUS_ICONS);
396 out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons));
397 out.endTag(null, TAG_STATUS_ICONS);
398 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400399
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400400 synchronized (mPackagePreferences) {
401 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400402 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400403 final PackagePreferences r = mPackagePreferences.valueAt(i);
Annie Meng8b646fd2019-02-01 18:46:42 +0000404 if (forBackup && UserHandle.getUserId(r.uid) != userId) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400405 continue;
406 }
407 final boolean hasNonDefaultSettings =
408 r.importance != DEFAULT_IMPORTANCE
409 || r.priority != DEFAULT_PRIORITY
410 || r.visibility != DEFAULT_VISIBILITY
411 || r.showBadge != DEFAULT_SHOW_BADGE
412 || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
413 || r.channels.size() > 0
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400414 || r.groups.size() > 0
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500415 || r.delegate != null
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800416 || r.allowBubble != DEFAULT_ALLOW_BUBBLE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400417 if (hasNonDefaultSettings) {
418 out.startTag(null, TAG_PACKAGE);
419 out.attribute(null, ATT_NAME, r.pkg);
420 if (r.importance != DEFAULT_IMPORTANCE) {
421 out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
422 }
423 if (r.priority != DEFAULT_PRIORITY) {
424 out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
425 }
426 if (r.visibility != DEFAULT_VISIBILITY) {
427 out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
428 }
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800429 if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) {
430 out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble));
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500431 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400432 out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
433 out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
434 Integer.toString(r.lockedAppFields));
435
436 if (!forBackup) {
437 out.attribute(null, ATT_UID, Integer.toString(r.uid));
438 }
439
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400440 if (r.delegate != null) {
441 out.startTag(null, TAG_DELEGATE);
442
443 out.attribute(null, ATT_NAME, r.delegate.mPkg);
444 out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
445 if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
446 out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
447 }
448 if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
449 out.attribute(null, ATT_USER_ALLOWED,
450 Boolean.toString(r.delegate.mUserAllowed));
451 }
452 out.endTag(null, TAG_DELEGATE);
453 }
454
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400455 for (NotificationChannelGroup group : r.groups.values()) {
456 group.writeXml(out);
457 }
458
459 for (NotificationChannel channel : r.channels.values()) {
460 if (forBackup) {
461 if (!channel.isDeleted()) {
462 channel.writeXmlForBackup(out, mContext);
463 }
464 } else {
465 channel.writeXml(out);
466 }
467 }
468
469 out.endTag(null, TAG_PACKAGE);
470 }
471 }
472 }
473 out.endTag(null, TAG_RANKING);
474 }
475
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800476 /**
477 * Sets whether bubbles are allowed.
478 *
479 * @param pkg the package to allow or not allow bubbles for.
480 * @param uid the uid to allow or not allow bubbles for.
481 * @param allowed whether bubbles are allowed.
482 */
483 public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
Mady Mellor9f296142019-05-24 09:42:52 -0700484 boolean changed = false;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400485 synchronized (mPackagePreferences) {
486 PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid);
Mady Mellor9f296142019-05-24 09:42:52 -0700487 changed = p.allowBubble != allowed;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400488 p.allowBubble = allowed;
489 p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
490 }
Mady Mellor9f296142019-05-24 09:42:52 -0700491 if (changed) {
492 updateConfig();
493 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500494 }
495
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800496 /**
497 * Whether bubbles are allowed.
498 *
499 * @param pkg the package to check if bubbles are allowed for
500 * @param uid the uid to check if bubbles are allowed for.
501 * @return whether bubbles are allowed.
502 */
Julia Reynolds4509ce72019-01-31 13:12:43 -0500503 @Override
Mady Mellor9db685a2019-01-23 13:23:37 -0800504 public boolean areBubblesAllowed(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400505 synchronized (mPackagePreferences) {
506 return getOrCreatePackagePreferencesLocked(pkg, uid).allowBubble;
507 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500508 }
509
510 public int getAppLockedFields(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400511 synchronized (mPackagePreferences) {
512 return getOrCreatePackagePreferencesLocked(pkg, uid).lockedAppFields;
513 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500514 }
515
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400516 /**
517 * Gets importance.
518 */
519 @Override
520 public int getImportance(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400521 synchronized (mPackagePreferences) {
522 return getOrCreatePackagePreferencesLocked(packageName, uid).importance;
523 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400524 }
525
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400526 /**
527 * Returns whether the importance of the corresponding notification is user-locked and shouldn't
528 * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
529 * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
530 */
531 public boolean getIsAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400532 synchronized (mPackagePreferences) {
533 int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields;
534 return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
535 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400536 }
537
538 @Override
539 public boolean canShowBadge(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400540 synchronized (mPackagePreferences) {
541 return getOrCreatePackagePreferencesLocked(packageName, uid).showBadge;
542 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400543 }
544
545 @Override
546 public void setShowBadge(String packageName, int uid, boolean showBadge) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400547 synchronized (mPackagePreferences) {
548 getOrCreatePackagePreferencesLocked(packageName, uid).showBadge = showBadge;
549 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400550 updateConfig();
551 }
552
553 @Override
554 public boolean isGroupBlocked(String packageName, int uid, String groupId) {
555 if (groupId == null) {
556 return false;
557 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400558 synchronized (mPackagePreferences) {
559 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
560 NotificationChannelGroup group = r.groups.get(groupId);
561 if (group == null) {
562 return false;
563 }
564 return group.isBlocked();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400565 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400566 }
567
568 int getPackagePriority(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400569 synchronized (mPackagePreferences) {
570 return getOrCreatePackagePreferencesLocked(pkg, uid).priority;
571 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400572 }
573
574 int getPackageVisibility(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400575 synchronized (mPackagePreferences) {
576 return getOrCreatePackagePreferencesLocked(pkg, uid).visibility;
577 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400578 }
579
580 @Override
581 public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
582 boolean fromTargetApp) {
583 Preconditions.checkNotNull(pkg);
584 Preconditions.checkNotNull(group);
585 Preconditions.checkNotNull(group.getId());
586 Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
Julia Reynolds5c399c62019-04-08 14:42:53 -0400587 synchronized (mPackagePreferences) {
588 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
589 if (r == null) {
590 throw new IllegalArgumentException("Invalid package");
591 }
592 final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
593 if (!group.equals(oldGroup)) {
594 // will log for new entries as well as name/description changes
595 MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
596 }
597 if (oldGroup != null) {
598 group.setChannels(oldGroup.getChannels());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400599
Julia Reynolds5c399c62019-04-08 14:42:53 -0400600 // apps can't update the blocked status or app overlay permission
601 if (fromTargetApp) {
602 group.setBlocked(oldGroup.isBlocked());
603 group.unlockFields(group.getUserLockedFields());
604 group.lockFields(oldGroup.getUserLockedFields());
605 } else {
606 // but the system can
607 if (group.isBlocked() != oldGroup.isBlocked()) {
608 group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
609 updateChannelsBypassingDnd(mContext.getUserId());
610 }
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -0400611 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400612 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400613 r.groups.put(group.getId(), group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400614 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400615 }
616
617 @Override
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400618 public boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400619 boolean fromTargetApp, boolean hasDndAccess) {
620 Preconditions.checkNotNull(pkg);
621 Preconditions.checkNotNull(channel);
622 Preconditions.checkNotNull(channel.getId());
623 Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400624 boolean needsPolicyFileChange = false;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400625 synchronized (mPackagePreferences) {
626 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
627 if (r == null) {
628 throw new IllegalArgumentException("Invalid package");
629 }
630 if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
631 throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
632 }
633 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
634 throw new IllegalArgumentException("Reserved id");
635 }
636 NotificationChannel existing = r.channels.get(channel.getId());
637 // Keep most of the existing settings
638 if (existing != null && fromTargetApp) {
639 if (existing.isDeleted()) {
640 existing.setDeleted(false);
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400641 needsPolicyFileChange = true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400642
Julia Reynolds5c399c62019-04-08 14:42:53 -0400643 // log a resurrected channel as if it's new again
644 MetricsLogger.action(getChannelLog(channel, pkg).setType(
645 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
646 }
647
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400648 if (!Objects.equals(channel.getName().toString(), existing.getName().toString())) {
649 existing.setName(channel.getName().toString());
650 needsPolicyFileChange = true;
651 }
652 if (!Objects.equals(channel.getDescription(), existing.getDescription())) {
653 existing.setDescription(channel.getDescription());
654 needsPolicyFileChange = true;
655 }
656 if (channel.isBlockableSystem() != existing.isBlockableSystem()) {
657 existing.setBlockableSystem(channel.isBlockableSystem());
658 needsPolicyFileChange = true;
659 }
660 if (channel.getGroup() != null && existing.getGroup() == null) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400661 existing.setGroup(channel.getGroup());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400662 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400663 }
664
665 // Apps are allowed to downgrade channel importance if the user has not changed any
666 // fields on this channel yet.
667 final int previousExistingImportance = existing.getImportance();
668 if (existing.getUserLockedFields() == 0 &&
669 channel.getImportance() < existing.getImportance()) {
670 existing.setImportance(channel.getImportance());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400671 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400672 }
673
674 // system apps and dnd access apps can bypass dnd if the user hasn't changed any
675 // fields on the channel yet
676 if (existing.getUserLockedFields() == 0 && hasDndAccess) {
677 boolean bypassDnd = channel.canBypassDnd();
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400678 if (bypassDnd != existing.canBypassDnd()) {
679 existing.setBypassDnd(bypassDnd);
680 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400681
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400682 if (bypassDnd != mAreChannelsBypassingDnd
683 || previousExistingImportance != existing.getImportance()) {
684 updateChannelsBypassingDnd(mContext.getUserId());
685 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400686 }
687 }
688
689 updateConfig();
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400690 return needsPolicyFileChange;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400691 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400692
693 needsPolicyFileChange = true;
694
Julia Reynolds5c399c62019-04-08 14:42:53 -0400695 if (channel.getImportance() < IMPORTANCE_NONE
696 || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
697 throw new IllegalArgumentException("Invalid importance level");
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400698 }
699
Julia Reynolds5c399c62019-04-08 14:42:53 -0400700 // Reset fields that apps aren't allowed to set.
701 if (fromTargetApp && !hasDndAccess) {
702 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400703 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400704 if (fromTargetApp) {
705 channel.setLockscreenVisibility(r.visibility);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400706 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400707 clearLockedFieldsLocked(channel);
708 channel.setImportanceLockedByOEM(r.oemLockedImportance);
709 if (!channel.isImportanceLockedByOEM()) {
710 if (r.futureOemLockedChannels.remove(channel.getId())) {
711 channel.setImportanceLockedByOEM(true);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400712 }
713 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400714 channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
715 if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
716 channel.setLockscreenVisibility(
717 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
Julia Reynolds413ba842019-01-11 10:38:08 -0500718 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400719 if (!r.showBadge) {
720 channel.setShowBadge(false);
721 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400722
Julia Reynolds5c399c62019-04-08 14:42:53 -0400723 r.channels.put(channel.getId(), channel);
724 if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
725 updateChannelsBypassingDnd(mContext.getUserId());
726 }
727 MetricsLogger.action(getChannelLog(channel, pkg).setType(
728 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400729 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400730
731 return needsPolicyFileChange;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400732 }
733
Julia Reynolds5c399c62019-04-08 14:42:53 -0400734 void clearLockedFieldsLocked(NotificationChannel channel) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400735 channel.unlockFields(channel.getUserLockedFields());
736 }
737
738 @Override
739 public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
740 boolean fromUser) {
741 Preconditions.checkNotNull(updatedChannel);
742 Preconditions.checkNotNull(updatedChannel.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400743 synchronized (mPackagePreferences) {
744 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
745 if (r == null) {
746 throw new IllegalArgumentException("Invalid package");
747 }
748 NotificationChannel channel = r.channels.get(updatedChannel.getId());
749 if (channel == null || channel.isDeleted()) {
750 throw new IllegalArgumentException("Channel does not exist");
751 }
752 if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
753 updatedChannel.setLockscreenVisibility(
754 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
755 }
756 if (fromUser) {
757 updatedChannel.lockFields(channel.getUserLockedFields());
758 lockFieldsForUpdateLocked(channel, updatedChannel);
759 } else {
760 updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
761 }
762 // no importance updates are allowed if OEM blocked it
763 updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
764 if (updatedChannel.isImportanceLockedByOEM()) {
765 updatedChannel.setImportance(channel.getImportance());
766 }
767 updatedChannel.setImportanceLockedByCriticalDeviceFunction(
768 r.defaultAppLockedImportance);
Beverly47679222019-05-16 15:46:11 -0400769 if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
770 && updatedChannel.getImportance() == IMPORTANCE_NONE) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400771 updatedChannel.setImportance(channel.getImportance());
772 }
Julia Reynolds413ba842019-01-11 10:38:08 -0500773
Julia Reynolds5c399c62019-04-08 14:42:53 -0400774 r.channels.put(updatedChannel.getId(), updatedChannel);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400775
Julia Reynolds5c399c62019-04-08 14:42:53 -0400776 if (onlyHasDefaultChannel(pkg, uid)) {
777 // copy settings to app level so they are inherited by new channels
778 // when the app migrates
779 r.importance = updatedChannel.getImportance();
780 r.priority = updatedChannel.canBypassDnd()
781 ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
782 r.visibility = updatedChannel.getLockscreenVisibility();
783 r.showBadge = updatedChannel.canShowBadge();
784 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400785
Julia Reynolds5c399c62019-04-08 14:42:53 -0400786 if (!channel.equals(updatedChannel)) {
787 // only log if there are real changes
788 MetricsLogger.action(getChannelLog(updatedChannel, pkg)
789 .setSubtype(fromUser ? 1 : 0));
790 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400791
Julia Reynolds5c399c62019-04-08 14:42:53 -0400792 if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
793 || channel.getImportance() != updatedChannel.getImportance()) {
794 updateChannelsBypassingDnd(mContext.getUserId());
795 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400796 }
797 updateConfig();
798 }
799
800 @Override
801 public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
802 boolean includeDeleted) {
803 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400804 synchronized (mPackagePreferences) {
805 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
806 if (r == null) {
807 return null;
808 }
809 if (channelId == null) {
810 channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
811 }
812 final NotificationChannel nc = r.channels.get(channelId);
813 if (nc != null && (includeDeleted || !nc.isDeleted())) {
814 return nc;
815 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400816 return null;
817 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400818 }
819
820 @Override
821 public void deleteNotificationChannel(String pkg, int uid, String channelId) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400822 synchronized (mPackagePreferences) {
823 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
824 if (r == null) {
825 return;
826 }
827 NotificationChannel channel = r.channels.get(channelId);
828 if (channel != null) {
829 channel.setDeleted(true);
830 LogMaker lm = getChannelLog(channel, pkg);
831 lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
832 MetricsLogger.action(lm);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400833
Julia Reynolds5c399c62019-04-08 14:42:53 -0400834 if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
835 updateChannelsBypassingDnd(mContext.getUserId());
836 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400837 }
838 }
839 }
840
841 @Override
842 @VisibleForTesting
843 public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
844 Preconditions.checkNotNull(pkg);
845 Preconditions.checkNotNull(channelId);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400846 synchronized (mPackagePreferences) {
847 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
848 if (r == null) {
849 return;
850 }
851 r.channels.remove(channelId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400852 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400853 }
854
855 @Override
856 public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
857 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400858 synchronized (mPackagePreferences) {
859 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
860 if (r == null) {
861 return;
862 }
863 int N = r.channels.size() - 1;
864 for (int i = N; i >= 0; i--) {
865 String key = r.channels.keyAt(i);
866 if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
867 r.channels.remove(key);
868 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400869 }
870 }
871 }
872
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500873 public boolean shouldHideSilentStatusIcons() {
874 return mHideSilentStatusBarIcons;
875 }
876
877 public void setHideSilentStatusIcons(boolean hide) {
878 mHideSilentStatusBarIcons = hide;
879 }
880
Julia Reynolds413ba842019-01-11 10:38:08 -0500881 public void lockChannelsForOEM(String[] appOrChannelList) {
882 if (appOrChannelList == null) {
883 return;
884 }
885 for (String appOrChannel : appOrChannelList) {
886 if (!TextUtils.isEmpty(appOrChannel)) {
887 String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
888 if (appSplit != null && appSplit.length > 0) {
889 String appName = appSplit[0];
890 String channelId = appSplit.length == 2 ? appSplit[1] : null;
891
892 synchronized (mPackagePreferences) {
893 for (PackagePreferences r : mPackagePreferences.values()) {
894 if (r.pkg.equals(appName)) {
895 if (channelId == null) {
896 // lock all channels for the app
897 r.oemLockedImportance = true;
898 for (NotificationChannel channel : r.channels.values()) {
899 channel.setImportanceLockedByOEM(true);
900 }
901 } else {
902 NotificationChannel channel = r.channels.get(channelId);
903 if (channel != null) {
904 channel.setImportanceLockedByOEM(true);
905 } else {
906 // if this channel shows up in the future, make sure it'll
907 // be locked immediately
908 r.futureOemLockedChannels.add(channelId);
909 }
910 }
911 }
912 }
913 }
914 }
915 }
916 }
917 }
918
Julia Reynoldse7ca31b2019-04-25 15:41:47 -0400919 public void updateDefaultApps(int userId, ArraySet<String> toRemove,
920 ArraySet<Pair<String, Integer>> toAdd) {
Julia Reynolds0c245002019-03-27 16:10:11 -0400921 synchronized (mPackagePreferences) {
922 for (PackagePreferences p : mPackagePreferences.values()) {
923 if (userId == UserHandle.getUserId(p.uid)) {
924 if (toRemove != null && toRemove.contains(p.pkg)) {
925 p.defaultAppLockedImportance = false;
926 for (NotificationChannel channel : p.channels.values()) {
927 channel.setImportanceLockedByCriticalDeviceFunction(false);
928 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -0400929 }
930 }
931 }
932 if (toAdd != null) {
933 for (Pair<String, Integer> approvedApp : toAdd) {
934 PackagePreferences p = getOrCreatePackagePreferencesLocked(approvedApp.first,
935 approvedApp.second);
936 p.defaultAppLockedImportance = true;
937 for (NotificationChannel channel : p.channels.values()) {
938 channel.setImportanceLockedByCriticalDeviceFunction(true);
Julia Reynolds0c245002019-03-27 16:10:11 -0400939 }
940 }
941 }
942 }
943 }
944
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400945 public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
946 int uid, String groupId, boolean includeDeleted) {
947 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400948 synchronized (mPackagePreferences) {
949 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
950 if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
951 return null;
952 }
953 NotificationChannelGroup group = r.groups.get(groupId).clone();
954 group.setChannels(new ArrayList<>());
955 int N = r.channels.size();
956 for (int i = 0; i < N; i++) {
957 final NotificationChannel nc = r.channels.valueAt(i);
958 if (includeDeleted || !nc.isDeleted()) {
959 if (groupId.equals(nc.getGroup())) {
960 group.addChannel(nc);
961 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400962 }
963 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400964 return group;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400965 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400966 }
967
968 public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
969 int uid) {
970 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400971 synchronized (mPackagePreferences) {
972 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
973 if (r == null) {
974 return null;
975 }
976 return r.groups.get(groupId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400977 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400978 }
979
980 @Override
981 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
Julia Reynolds13ed28b2018-09-21 15:20:13 -0400982 int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400983 Preconditions.checkNotNull(pkg);
984 Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -0400985 synchronized (mPackagePreferences) {
986 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
987 if (r == null) {
988 return ParceledListSlice.emptyList();
989 }
990 NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
991 int N = r.channels.size();
992 for (int i = 0; i < N; i++) {
993 final NotificationChannel nc = r.channels.valueAt(i);
994 if (includeDeleted || !nc.isDeleted()) {
995 if (nc.getGroup() != null) {
996 if (r.groups.get(nc.getGroup()) != null) {
997 NotificationChannelGroup ncg = groups.get(nc.getGroup());
998 if (ncg == null) {
999 ncg = r.groups.get(nc.getGroup()).clone();
1000 ncg.setChannels(new ArrayList<>());
1001 groups.put(nc.getGroup(), ncg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001002
Julia Reynolds5c399c62019-04-08 14:42:53 -04001003 }
1004 ncg.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001005 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001006 } else {
1007 nonGrouped.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001008 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001009 }
1010 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001011 if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
1012 groups.put(null, nonGrouped);
1013 }
1014 if (includeEmpty) {
1015 for (NotificationChannelGroup group : r.groups.values()) {
1016 if (!groups.containsKey(group.getId())) {
1017 groups.put(group.getId(), group);
1018 }
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001019 }
1020 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001021 return new ParceledListSlice<>(new ArrayList<>(groups.values()));
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001022 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001023 }
1024
1025 public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
1026 String groupId) {
1027 List<NotificationChannel> deletedChannels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001028 synchronized (mPackagePreferences) {
1029 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1030 if (r == null || TextUtils.isEmpty(groupId)) {
1031 return deletedChannels;
1032 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001033
Julia Reynolds5c399c62019-04-08 14:42:53 -04001034 r.groups.remove(groupId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001035
Julia Reynolds5c399c62019-04-08 14:42:53 -04001036 int N = r.channels.size();
1037 for (int i = 0; i < N; i++) {
1038 final NotificationChannel nc = r.channels.valueAt(i);
1039 if (groupId.equals(nc.getGroup())) {
1040 nc.setDeleted(true);
1041 deletedChannels.add(nc);
1042 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001043 }
1044 }
1045 return deletedChannels;
1046 }
1047
1048 @Override
1049 public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
1050 int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001051 List<NotificationChannelGroup> groups = new ArrayList<>();
1052 synchronized (mPackagePreferences) {
1053 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1054 if (r == null) {
1055 return groups;
1056 }
1057 groups.addAll(r.groups.values());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001058 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001059 return groups;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001060 }
1061
1062 @Override
1063 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
1064 boolean includeDeleted) {
1065 Preconditions.checkNotNull(pkg);
1066 List<NotificationChannel> channels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001067 synchronized (mPackagePreferences) {
1068 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1069 if (r == null) {
1070 return ParceledListSlice.emptyList();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001071 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001072 int N = r.channels.size();
1073 for (int i = 0; i < N; i++) {
1074 final NotificationChannel nc = r.channels.valueAt(i);
1075 if (includeDeleted || !nc.isDeleted()) {
1076 channels.add(nc);
1077 }
1078 }
1079 return new ParceledListSlice<>(channels);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001080 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001081 }
1082
1083 /**
Beverly0479cde22018-11-09 11:05:34 -05001084 * Gets all notification channels associated with the given pkg and userId that can bypass dnd
1085 */
1086 public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
1087 int userId) {
1088 List<NotificationChannel> channels = new ArrayList<>();
1089 synchronized (mPackagePreferences) {
1090 final PackagePreferences r = mPackagePreferences.get(
1091 packagePreferencesKey(pkg, userId));
1092 // notifications from this package aren't blocked
1093 if (r != null && r.importance != IMPORTANCE_NONE) {
1094 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001095 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001096 channels.add(channel);
1097 }
1098 }
1099 }
1100 }
1101 return new ParceledListSlice<>(channels);
1102 }
1103
1104 /**
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001105 * True for pre-O apps that only have the default channel, or pre O apps that have no
1106 * channels yet. This method will create the default channel for pre-O apps that don't have it.
1107 * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
1108 * upgrades.
1109 */
1110 public boolean onlyHasDefaultChannel(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001111 synchronized (mPackagePreferences) {
1112 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1113 if (r.channels.size() == 1
1114 && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
1115 return true;
1116 }
1117 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001118 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001119 }
1120
1121 public int getDeletedChannelCount(String pkg, int uid) {
1122 Preconditions.checkNotNull(pkg);
1123 int deletedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001124 synchronized (mPackagePreferences) {
1125 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1126 if (r == null) {
1127 return deletedCount;
1128 }
1129 int N = r.channels.size();
1130 for (int i = 0; i < N; i++) {
1131 final NotificationChannel nc = r.channels.valueAt(i);
1132 if (nc.isDeleted()) {
1133 deletedCount++;
1134 }
1135 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001136 return deletedCount;
1137 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001138 }
1139
1140 public int getBlockedChannelCount(String pkg, int uid) {
1141 Preconditions.checkNotNull(pkg);
1142 int blockedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001143 synchronized (mPackagePreferences) {
1144 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1145 if (r == null) {
1146 return blockedCount;
1147 }
1148 int N = r.channels.size();
1149 for (int i = 0; i < N; i++) {
1150 final NotificationChannel nc = r.channels.valueAt(i);
1151 if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
1152 blockedCount++;
1153 }
1154 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001155 return blockedCount;
1156 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001157 }
1158
1159 public int getBlockedAppCount(int userId) {
1160 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001161 synchronized (mPackagePreferences) {
1162 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001163 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001164 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001165 if (userId == UserHandle.getUserId(r.uid)
1166 && r.importance == IMPORTANCE_NONE) {
1167 count++;
1168 }
1169 }
1170 }
1171 return count;
1172 }
1173
Beverly0479cde22018-11-09 11:05:34 -05001174 /**
1175 * Returns the number of apps that have at least one notification channel that can bypass DND
1176 * for given particular user
1177 */
1178 public int getAppsBypassingDndCount(int userId) {
1179 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001180 synchronized (mPackagePreferences) {
Beverly0479cde22018-11-09 11:05:34 -05001181 final int numPackagePreferences = mPackagePreferences.size();
1182 for (int i = 0; i < numPackagePreferences; i++) {
1183 final PackagePreferences r = mPackagePreferences.valueAt(i);
1184 // Package isn't associated with this userId or notifications from this package are
1185 // blocked
1186 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1187 continue;
1188 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001189
Beverly0479cde22018-11-09 11:05:34 -05001190 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001191 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001192 count++;
1193 break;
1194 }
1195 }
1196 }
1197 }
1198 return count;
1199 }
1200
1201 /**
1202 * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
1203 * updating
1204 * @param userId
1205 */
1206 private void syncChannelsBypassingDnd(int userId) {
1207 mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
1208 & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
1209 updateChannelsBypassingDnd(userId);
1210 }
1211
1212 /**
1213 * Updates the user's NotificationPolicy based on whether the given userId
1214 * has channels bypassing DND
1215 * @param userId
1216 */
1217 private void updateChannelsBypassingDnd(int userId) {
1218 synchronized (mPackagePreferences) {
1219 final int numPackagePreferences = mPackagePreferences.size();
1220 for (int i = 0; i < numPackagePreferences; i++) {
1221 final PackagePreferences r = mPackagePreferences.valueAt(i);
1222 // Package isn't associated with this userId or notifications from this package are
1223 // blocked
1224 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1225 continue;
1226 }
1227
1228 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001229 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001230 if (!mAreChannelsBypassingDnd) {
1231 mAreChannelsBypassingDnd = true;
1232 updateZenPolicy(true);
1233 }
1234 return;
1235 }
1236 }
1237 }
1238 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001239 // If no channels bypass DND, update the zen policy once to disable DND bypass.
1240 if (mAreChannelsBypassingDnd) {
1241 mAreChannelsBypassingDnd = false;
1242 updateZenPolicy(false);
1243 }
1244 }
1245
Julia Reynolds5c399c62019-04-08 14:42:53 -04001246 private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
Beverly0479cde22018-11-09 11:05:34 -05001247 // Channel is in a group that's blocked
Beverly4f7b53d2018-11-20 09:56:31 -05001248 if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
1249 return false;
Beverly0479cde22018-11-09 11:05:34 -05001250 }
1251
1252 // Channel is deleted or is blocked
1253 if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
1254 return false;
1255 }
1256
1257 return true;
1258 }
1259
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001260 public void updateZenPolicy(boolean areChannelsBypassingDnd) {
1261 NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
1262 mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
1263 policy.priorityCategories, policy.priorityCallSenders,
1264 policy.priorityMessageSenders, policy.suppressedVisualEffects,
1265 (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
1266 : 0)));
1267 }
1268
1269 public boolean areChannelsBypassingDnd() {
1270 return mAreChannelsBypassingDnd;
1271 }
1272
1273 /**
1274 * Sets importance.
1275 */
1276 @Override
1277 public void setImportance(String pkgName, int uid, int importance) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001278 synchronized (mPackagePreferences) {
1279 getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance;
1280 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001281 updateConfig();
1282 }
1283
1284 public void setEnabled(String packageName, int uid, boolean enabled) {
1285 boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
1286 if (wasEnabled == enabled) {
1287 return;
1288 }
1289 setImportance(packageName, uid,
1290 enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
1291 }
1292
1293 /**
1294 * Sets whether any notifications from the app, represented by the given {@code pkgName} and
1295 * {@code uid}, have their importance locked by the user. Locked notifications don't get
1296 * considered for sentiment adjustments (and thus never show a blocking helper).
1297 */
1298 public void setAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001299 synchronized (mPackagePreferences) {
1300 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(packageName, uid);
1301 if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
1302 return;
1303 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001304
Julia Reynolds5c399c62019-04-08 14:42:53 -04001305 prefs.lockedAppFields =
1306 prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
1307 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001308 updateConfig();
1309 }
1310
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001311 /**
1312 * Returns the delegate for a given package, if it's allowed by the package and the user.
1313 */
1314 public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001315 synchronized (mPackagePreferences) {
1316 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001317
Julia Reynolds5c399c62019-04-08 14:42:53 -04001318 if (prefs == null || prefs.delegate == null) {
1319 return null;
1320 }
1321 if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
1322 return null;
1323 }
1324 return prefs.delegate.mPkg;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001325 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001326 }
1327
1328 /**
1329 * Used by an app to delegate notification posting privileges to another apps.
1330 */
1331 public void setNotificationDelegate(String sourcePkg, int sourceUid,
1332 String delegatePkg, int delegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001333 synchronized (mPackagePreferences) {
1334 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001335
Julia Reynolds5c399c62019-04-08 14:42:53 -04001336 boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
1337 Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
1338 prefs.delegate = delegate;
1339 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001340 updateConfig();
1341 }
1342
1343 /**
1344 * Used by an app to turn off its notification delegate.
1345 */
1346 public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001347 boolean changed = false;
1348 synchronized (mPackagePreferences) {
1349 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1350 if (prefs != null && prefs.delegate != null) {
1351 prefs.delegate.mEnabled = false;
1352 changed = true;
1353 }
1354 }
1355 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001356 updateConfig();
1357 }
1358 }
1359
1360 /**
1361 * Toggles whether an app can have a notification delegate on behalf of a user.
1362 */
1363 public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001364 boolean changed = false;
1365 synchronized (mPackagePreferences) {
1366 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1367 if (prefs != null && prefs.delegate != null) {
1368 prefs.delegate.mUserAllowed = userAllowed;
1369 changed = true;
1370 }
1371 }
1372 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001373 updateConfig();
1374 }
1375 }
1376
1377 /**
1378 * Returns whether the given app is allowed on post notifications on behalf of the other given
1379 * app.
1380 */
1381 public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
1382 String potentialDelegatePkg, int potentialDelegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001383 synchronized (mPackagePreferences) {
1384 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001385
Julia Reynolds5c399c62019-04-08 14:42:53 -04001386 return prefs != null && prefs.isValidDelegate(potentialDelegatePkg,
1387 potentialDelegateUid);
1388 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001389 }
1390
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001391 @VisibleForTesting
Julia Reynolds5c399c62019-04-08 14:42:53 -04001392 void lockFieldsForUpdateLocked(NotificationChannel original, NotificationChannel update) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001393 if (original.canBypassDnd() != update.canBypassDnd()) {
1394 update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
1395 }
1396 if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
1397 update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
1398 }
1399 if (original.getImportance() != update.getImportance()) {
1400 update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
1401 }
1402 if (original.shouldShowLights() != update.shouldShowLights()
1403 || original.getLightColor() != update.getLightColor()) {
1404 update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
1405 }
1406 if (!Objects.equals(original.getSound(), update.getSound())) {
1407 update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
1408 }
1409 if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
1410 || original.shouldVibrate() != update.shouldVibrate()) {
1411 update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
1412 }
1413 if (original.canShowBadge() != update.canShowBadge()) {
1414 update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
1415 }
Julia Reynolds4509ce72019-01-31 13:12:43 -05001416 if (original.canBubble() != update.canBubble()) {
Mady Mellorc39b4ae2019-01-09 17:11:37 -08001417 update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE);
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -04001418 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001419 }
1420
1421 public void dump(PrintWriter pw, String prefix,
1422 @NonNull NotificationManagerService.DumpFilter filter) {
1423 pw.print(prefix);
1424 pw.println("per-package config:");
1425
Julia Reynolds5c399c62019-04-08 14:42:53 -04001426 pw.println("PackagePreferences:");
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001427 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001428 dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001429 }
1430 pw.println("Restored without uid:");
Julia Reynolds5c399c62019-04-08 14:42:53 -04001431 dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001432 }
1433
1434 public void dump(ProtoOutputStream proto,
1435 @NonNull NotificationManagerService.DumpFilter filter) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001436 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001437 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001438 mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001439 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001440 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001441 mRestoredWithoutUids);
1442 }
1443
Julia Reynolds5c399c62019-04-08 14:42:53 -04001444 private static void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001445 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001446 ArrayMap<String, PackagePreferences> packagePreferences) {
1447 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001448 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001449 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001450 if (filter.matches(r.pkg)) {
1451 pw.print(prefix);
1452 pw.print(" AppSettings: ");
1453 pw.print(r.pkg);
1454 pw.print(" (");
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001455 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001456 pw.print(')');
1457 if (r.importance != DEFAULT_IMPORTANCE) {
1458 pw.print(" importance=");
1459 pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
1460 }
1461 if (r.priority != DEFAULT_PRIORITY) {
1462 pw.print(" priority=");
1463 pw.print(Notification.priorityToString(r.priority));
1464 }
1465 if (r.visibility != DEFAULT_VISIBILITY) {
1466 pw.print(" visibility=");
1467 pw.print(Notification.visibilityToString(r.visibility));
1468 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001469 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1470 pw.print(" showBadge=");
1471 pw.print(r.showBadge);
1472 }
1473 if (r.defaultAppLockedImportance != DEFAULT_APP_LOCKED_IMPORTANCE) {
1474 pw.print(" defaultAppLocked=");
1475 pw.print(r.defaultAppLockedImportance);
1476 }
1477 if (r.oemLockedImportance != DEFAULT_OEM_LOCKED_IMPORTANCE) {
1478 pw.print(" oemLocked=");
1479 pw.print(r.oemLockedImportance);
1480 }
1481 if (!r.futureOemLockedChannels.isEmpty()) {
1482 pw.print(" futureLockedChannels=");
1483 pw.print(r.futureOemLockedChannels);
1484 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001485 pw.println();
1486 for (NotificationChannel channel : r.channels.values()) {
1487 pw.print(prefix);
1488 channel.dump(pw, " ", filter.redact);
1489 }
1490 for (NotificationChannelGroup group : r.groups.values()) {
1491 pw.print(prefix);
1492 pw.print(" ");
1493 pw.print(" ");
1494 pw.println(group);
1495 }
1496 }
1497 }
1498 }
1499
Julia Reynolds5c399c62019-04-08 14:42:53 -04001500 private static void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001501 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001502 ArrayMap<String, PackagePreferences> packagePreferences) {
1503 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001504 long fToken;
1505 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001506 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001507 if (filter.matches(r.pkg)) {
1508 fToken = proto.start(fieldId);
1509
1510 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
1511 proto.write(RankingHelperProto.RecordProto.UID, r.uid);
1512 proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
1513 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
1514 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
1515 proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
1516
1517 for (NotificationChannel channel : r.channels.values()) {
1518 channel.writeToProto(proto, RankingHelperProto.RecordProto.CHANNELS);
1519 }
1520 for (NotificationChannelGroup group : r.groups.values()) {
1521 group.writeToProto(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS);
1522 }
1523
1524 proto.end(fToken);
1525 }
1526 }
1527 }
1528
1529 public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
1530 JSONObject ranking = new JSONObject();
1531 JSONArray PackagePreferencess = new JSONArray();
1532 try {
1533 ranking.put("noUid", mRestoredWithoutUids.size());
1534 } catch (JSONException e) {
1535 // pass
1536 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001537 synchronized (mPackagePreferences) {
1538 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001539 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001540 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001541 if (filter == null || filter.matches(r.pkg)) {
1542 JSONObject PackagePreferences = new JSONObject();
1543 try {
1544 PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
1545 PackagePreferences.put("packageName", r.pkg);
1546 if (r.importance != DEFAULT_IMPORTANCE) {
1547 PackagePreferences.put("importance",
1548 NotificationListenerService.Ranking.importanceToString(
1549 r.importance));
1550 }
1551 if (r.priority != DEFAULT_PRIORITY) {
1552 PackagePreferences.put("priority",
1553 Notification.priorityToString(r.priority));
1554 }
1555 if (r.visibility != DEFAULT_VISIBILITY) {
1556 PackagePreferences.put("visibility",
1557 Notification.visibilityToString(r.visibility));
1558 }
1559 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1560 PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
1561 }
1562 JSONArray channels = new JSONArray();
1563 for (NotificationChannel channel : r.channels.values()) {
1564 channels.put(channel.toJson());
1565 }
1566 PackagePreferences.put("channels", channels);
1567 JSONArray groups = new JSONArray();
1568 for (NotificationChannelGroup group : r.groups.values()) {
1569 groups.put(group.toJson());
1570 }
1571 PackagePreferences.put("groups", groups);
1572 } catch (JSONException e) {
1573 // pass
1574 }
1575 PackagePreferencess.put(PackagePreferences);
1576 }
1577 }
1578 }
1579 try {
1580 ranking.put("PackagePreferencess", PackagePreferencess);
1581 } catch (JSONException e) {
1582 // pass
1583 }
1584 return ranking;
1585 }
1586
1587 /**
1588 * Dump only the ban information as structured JSON for the stats collector.
1589 *
1590 * This is intentionally redundant with {#link dumpJson} because the old
1591 * scraper will expect this format.
1592 *
1593 * @param filter
1594 * @return
1595 */
1596 public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
1597 JSONArray bans = new JSONArray();
1598 Map<Integer, String> packageBans = getPackageBans();
1599 for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
1600 final int userId = UserHandle.getUserId(ban.getKey());
1601 final String packageName = ban.getValue();
1602 if (filter == null || filter.matches(packageName)) {
1603 JSONObject banJson = new JSONObject();
1604 try {
1605 banJson.put("userId", userId);
1606 banJson.put("packageName", packageName);
1607 } catch (JSONException e) {
1608 e.printStackTrace();
1609 }
1610 bans.put(banJson);
1611 }
1612 }
1613 return bans;
1614 }
1615
1616 public Map<Integer, String> getPackageBans() {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001617 synchronized (mPackagePreferences) {
1618 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001619 ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
1620 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001621 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001622 if (r.importance == IMPORTANCE_NONE) {
1623 packageBans.put(r.uid, r.pkg);
1624 }
1625 }
1626
1627 return packageBans;
1628 }
1629 }
1630
1631 /**
1632 * Dump only the channel information as structured JSON for the stats collector.
1633 *
1634 * This is intentionally redundant with {#link dumpJson} because the old
1635 * scraper will expect this format.
1636 *
1637 * @param filter
1638 * @return
1639 */
1640 public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
1641 JSONArray channels = new JSONArray();
1642 Map<String, Integer> packageChannels = getPackageChannels();
1643 for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) {
1644 final String packageName = channelCount.getKey();
1645 if (filter == null || filter.matches(packageName)) {
1646 JSONObject channelCountJson = new JSONObject();
1647 try {
1648 channelCountJson.put("packageName", packageName);
1649 channelCountJson.put("channelCount", channelCount.getValue());
1650 } catch (JSONException e) {
1651 e.printStackTrace();
1652 }
1653 channels.put(channelCountJson);
1654 }
1655 }
1656 return channels;
1657 }
1658
1659 private Map<String, Integer> getPackageChannels() {
1660 ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001661 synchronized (mPackagePreferences) {
1662 for (int i = 0; i < mPackagePreferences.size(); i++) {
1663 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001664 int channelCount = 0;
1665 for (int j = 0; j < r.channels.size(); j++) {
1666 if (!r.channels.valueAt(j).isDeleted()) {
1667 channelCount++;
1668 }
1669 }
1670 packageChannels.put(r.pkg, channelCount);
1671 }
1672 }
1673 return packageChannels;
1674 }
1675
Beverly0479cde22018-11-09 11:05:34 -05001676 /**
1677 * Called when user switches
1678 */
1679 public void onUserSwitched(int userId) {
1680 syncChannelsBypassingDnd(userId);
1681 }
1682
1683 /**
1684 * Called when user is unlocked
1685 */
1686 public void onUserUnlocked(int userId) {
1687 syncChannelsBypassingDnd(userId);
1688 }
1689
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001690 public void onUserRemoved(int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001691 synchronized (mPackagePreferences) {
1692 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001693 for (int i = N - 1; i >= 0; i--) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001694 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001695 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001696 mPackagePreferences.removeAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001697 }
1698 }
1699 }
1700 }
1701
1702 protected void onLocaleChanged(Context context, int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001703 synchronized (mPackagePreferences) {
1704 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001705 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001706 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001707 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
1708 if (PackagePreferences.channels.containsKey(
1709 NotificationChannel.DEFAULT_CHANNEL_ID)) {
1710 PackagePreferences.channels.get(
1711 NotificationChannel.DEFAULT_CHANNEL_ID).setName(
1712 context.getResources().getString(
1713 R.string.default_notification_channel_label));
1714 }
1715 }
1716 }
1717 }
1718 }
1719
Julia Reynolds996c7c12019-05-24 10:25:33 -04001720 public boolean onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001721 int[] uidList) {
1722 if (pkgList == null || pkgList.length == 0) {
Julia Reynolds996c7c12019-05-24 10:25:33 -04001723 return false; // nothing to do
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001724 }
1725 boolean updated = false;
1726 if (removingPackage) {
1727 // Remove notification settings for uninstalled package
1728 int size = Math.min(pkgList.length, uidList.length);
1729 for (int i = 0; i < size; i++) {
1730 final String pkg = pkgList[i];
1731 final int uid = uidList[i];
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001732 synchronized (mPackagePreferences) {
1733 mPackagePreferences.remove(packagePreferencesKey(pkg, uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001734 }
1735 mRestoredWithoutUids.remove(pkg);
1736 updated = true;
1737 }
1738 } else {
1739 for (String pkg : pkgList) {
1740 // Package install
1741 final PackagePreferences r = mRestoredWithoutUids.get(pkg);
1742 if (r != null) {
1743 try {
1744 r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
1745 mRestoredWithoutUids.remove(pkg);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001746 synchronized (mPackagePreferences) {
1747 mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001748 }
1749 updated = true;
1750 } catch (PackageManager.NameNotFoundException e) {
1751 // noop
1752 }
1753 }
1754 // Package upgrade
1755 try {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001756 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001757 PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001758 mPm.getPackageUidAsUser(pkg, changeUserId));
1759 if (fullPackagePreferences != null) {
Julia Reynolds996c7c12019-05-24 10:25:33 -04001760 updated |= createDefaultChannelIfNeededLocked(fullPackagePreferences);
1761 updated |= deleteDefaultChannelIfNeededLocked(fullPackagePreferences);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001762 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001763 }
1764 } catch (PackageManager.NameNotFoundException e) {
1765 }
1766 }
1767 }
1768
1769 if (updated) {
1770 updateConfig();
1771 }
Julia Reynolds996c7c12019-05-24 10:25:33 -04001772 return updated;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001773 }
1774
Julia Reynolds7af51c52019-04-19 11:08:27 -04001775 public void clearData(String pkg, int uid) {
1776 synchronized (mPackagePreferences) {
1777 PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
1778 if (p != null) {
1779 p.channels = new ArrayMap<>();
1780 p.groups = new ArrayMap<>();
1781 p.delegate = null;
1782 p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
1783 p.allowBubble = DEFAULT_ALLOW_BUBBLE;
1784 p.importance = DEFAULT_IMPORTANCE;
1785 p.priority = DEFAULT_PRIORITY;
1786 p.visibility = DEFAULT_VISIBILITY;
1787 p.showBadge = DEFAULT_SHOW_BADGE;
1788 }
1789 }
1790 }
1791
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001792 private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
1793 return new LogMaker(
1794 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1795 .ACTION_NOTIFICATION_CHANNEL)
1796 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1797 .setPackageName(pkg)
1798 .addTaggedData(
1799 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1800 .FIELD_NOTIFICATION_CHANNEL_ID,
1801 channel.getId())
1802 .addTaggedData(
1803 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1804 .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
1805 channel.getImportance());
1806 }
1807
1808 private LogMaker getChannelGroupLog(String groupId, String pkg) {
1809 return new LogMaker(
1810 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1811 .ACTION_NOTIFICATION_CHANNEL_GROUP)
1812 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
1813 .addTaggedData(
1814 com.android.internal.logging.nano.MetricsProto.MetricsEvent
1815 .FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
1816 groupId)
1817 .setPackageName(pkg);
1818 }
1819
Julia Reynolds4509ce72019-01-31 13:12:43 -05001820 public void updateBubblesEnabled() {
1821 if (mBubblesEnabled == null) {
1822 mBubblesEnabled = new SparseBooleanArray();
1823 }
1824 boolean changed = false;
1825 // update the cached values
1826 for (int index = 0; index < mBubblesEnabled.size(); index++) {
1827 int userId = mBubblesEnabled.keyAt(index);
1828 final boolean oldValue = mBubblesEnabled.get(userId);
1829 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1830 Settings.Secure.NOTIFICATION_BUBBLES,
1831 DEFAULT_ALLOW_BUBBLE ? 1 : 0, userId) != 0;
1832 mBubblesEnabled.put(userId, newValue);
1833 changed |= oldValue != newValue;
1834 }
1835 if (changed) {
1836 updateConfig();
1837 }
1838 }
1839
1840 public boolean bubblesEnabled(UserHandle userHandle) {
1841 int userId = userHandle.getIdentifier();
1842 if (userId == UserHandle.USER_ALL) {
1843 return false;
1844 }
1845 if (mBubblesEnabled.indexOfKey(userId) < 0) {
1846 mBubblesEnabled.put(userId,
1847 Settings.Secure.getIntForUser(mContext.getContentResolver(),
1848 Settings.Secure.NOTIFICATION_BUBBLES,
1849 DEFAULT_ALLOW_BUBBLE ? 1 : 0, userId) != 0);
1850 }
1851 return mBubblesEnabled.get(userId, DEFAULT_ALLOW_BUBBLE);
1852 }
1853
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001854
1855 public void updateBadgingEnabled() {
1856 if (mBadgingEnabled == null) {
1857 mBadgingEnabled = new SparseBooleanArray();
1858 }
1859 boolean changed = false;
1860 // update the cached values
1861 for (int index = 0; index < mBadgingEnabled.size(); index++) {
1862 int userId = mBadgingEnabled.keyAt(index);
1863 final boolean oldValue = mBadgingEnabled.get(userId);
1864 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1865 Settings.Secure.NOTIFICATION_BADGING,
1866 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
1867 mBadgingEnabled.put(userId, newValue);
1868 changed |= oldValue != newValue;
1869 }
1870 if (changed) {
1871 updateConfig();
1872 }
1873 }
1874
1875 public boolean badgingEnabled(UserHandle userHandle) {
1876 int userId = userHandle.getIdentifier();
1877 if (userId == UserHandle.USER_ALL) {
1878 return false;
1879 }
1880 if (mBadgingEnabled.indexOfKey(userId) < 0) {
1881 mBadgingEnabled.put(userId,
1882 Settings.Secure.getIntForUser(mContext.getContentResolver(),
1883 Settings.Secure.NOTIFICATION_BADGING,
1884 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
1885 }
1886 return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
1887 }
1888
1889 private void updateConfig() {
1890 mRankingHandler.requestSort();
1891 }
1892
1893 private static String packagePreferencesKey(String pkg, int uid) {
1894 return pkg + "|" + uid;
1895 }
1896
1897 private static class PackagePreferences {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001898 String pkg;
1899 int uid = UNKNOWN_UID;
1900 int importance = DEFAULT_IMPORTANCE;
1901 int priority = DEFAULT_PRIORITY;
1902 int visibility = DEFAULT_VISIBILITY;
1903 boolean showBadge = DEFAULT_SHOW_BADGE;
Mady Mellorc39b4ae2019-01-09 17:11:37 -08001904 boolean allowBubble = DEFAULT_ALLOW_BUBBLE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001905 int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Julia Reynolds0c245002019-03-27 16:10:11 -04001906 // these fields are loaded on boot from a different source of truth and so are not
1907 // written to notification policy xml
Julia Reynolds413ba842019-01-11 10:38:08 -05001908 boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
1909 List<String> futureOemLockedChannels = new ArrayList<>();
Julia Reynolds0c245002019-03-27 16:10:11 -04001910 boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001911
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001912 Delegate delegate = null;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001913 ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
1914 Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001915
1916 public boolean isValidDelegate(String pkg, int uid) {
1917 return delegate != null && delegate.isAllowed(pkg, uid);
1918 }
1919 }
1920
1921 private static class Delegate {
1922 static final boolean DEFAULT_ENABLED = true;
1923 static final boolean DEFAULT_USER_ALLOWED = true;
1924 String mPkg;
1925 int mUid = UNKNOWN_UID;
1926 boolean mEnabled = DEFAULT_ENABLED;
1927 boolean mUserAllowed = DEFAULT_USER_ALLOWED;
1928
1929 Delegate(String pkg, int uid, boolean enabled, boolean userAllowed) {
1930 mPkg = pkg;
1931 mUid = uid;
1932 mEnabled = enabled;
1933 mUserAllowed = userAllowed;
1934 }
1935
1936 public boolean isAllowed(String pkg, int uid) {
1937 if (pkg == null || uid == UNKNOWN_UID) {
1938 return false;
1939 }
1940 return pkg.equals(mPkg)
1941 && uid == mUid
1942 && (mUserAllowed && mEnabled);
1943 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001944 }
1945}