blob: 95c48c2fbb1f88f40d196659bc559dffe94d62d5 [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
Mady Mellorc888d512020-04-09 14:48:02 -070019import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
Julia Reynolds7c267522020-01-16 11:26:41 -050020import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
Mady Mellorc888d512020-04-09 14:48:02 -070021import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
Mady Mellora92268c2020-03-09 17:25:08 -070022import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040023import static android.app.NotificationManager.IMPORTANCE_NONE;
Julia Reynolds469144c2019-06-21 14:30:28 -040024import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040025
Muhammad Qureshi22e52da2020-03-30 21:14:21 -070026import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
Yotam Aron74299972020-01-16 16:20:58 +020027import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
28import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
29import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
30
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040031import android.annotation.IntDef;
32import android.annotation.NonNull;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040033import android.annotation.Nullable;
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -050034import android.annotation.UserIdInt;
Mady Mellorc888d512020-04-09 14:48:02 -070035import android.app.AppOpsManager;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040036import android.app.Notification;
37import android.app.NotificationChannel;
38import android.app.NotificationChannelGroup;
39import android.app.NotificationManager;
40import android.content.Context;
41import android.content.pm.ApplicationInfo;
42import android.content.pm.PackageManager;
43import android.content.pm.ParceledListSlice;
44import android.metrics.LogMaker;
45import android.os.Build;
46import android.os.UserHandle;
47import android.provider.Settings;
Julia Reynolds882f2062020-02-05 12:11:38 -050048import android.service.notification.ConversationChannelWrapper;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040049import android.service.notification.NotificationListenerService;
50import android.service.notification.RankingHelperProto;
51import android.text.TextUtils;
52import android.util.ArrayMap;
Julia Reynolds0c245002019-03-27 16:10:11 -040053import android.util.ArraySet;
Julia Reynoldse7ca31b2019-04-25 15:41:47 -040054import android.util.Pair;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040055import android.util.Slog;
56import android.util.SparseBooleanArray;
Yotam Aron74299972020-01-16 16:20:58 +020057import android.util.StatsEvent;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040058import android.util.proto.ProtoOutputStream;
59
60import com.android.internal.R;
61import com.android.internal.annotations.VisibleForTesting;
62import com.android.internal.logging.MetricsLogger;
63import com.android.internal.util.Preconditions;
64import com.android.internal.util.XmlUtils;
65
66import org.json.JSONArray;
67import org.json.JSONException;
68import org.json.JSONObject;
69import org.xmlpull.v1.XmlPullParser;
70import org.xmlpull.v1.XmlPullParserException;
71import org.xmlpull.v1.XmlSerializer;
72
73import java.io.IOException;
74import java.io.PrintWriter;
75import java.util.ArrayList;
76import java.util.Arrays;
77import java.util.Collection;
78import java.util.List;
79import java.util.Map;
80import java.util.Objects;
81import java.util.concurrent.ConcurrentHashMap;
82
83public class PreferencesHelper implements RankingConfig {
84 private static final String TAG = "NotificationPrefHelper";
Mady Mellorc888d512020-04-09 14:48:02 -070085 private static final int XML_VERSION = 2;
86 /** What version to check to do the upgrade for bubbles. */
87 private static final int XML_VERSION_BUBBLES_UPGRADE = 1;
Mady Mellorbccdf452020-05-27 11:57:12 -070088 @VisibleForTesting
89 static final int UNKNOWN_UID = UserHandle.USER_NULL;
Julia Reynolds413ba842019-01-11 10:38:08 -050090 private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040091
92 @VisibleForTesting
Julia Reynolds7dbaa072020-06-18 09:37:09 -040093 static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 50000;
Julia Reynoldsc29370a2019-08-20 16:08:42 -040094
Yotam Aron74299972020-01-16 16:20:58 +020095 private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000;
96 private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000;
97 private static final int NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT = 1000;
98
Julia Reynoldsc29370a2019-08-20 16:08:42 -040099 @VisibleForTesting
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400100 static final String TAG_RANKING = "ranking";
101 private static final String TAG_PACKAGE = "package";
102 private static final String TAG_CHANNEL = "channel";
103 private static final String TAG_GROUP = "channelGroup";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400104 private static final String TAG_DELEGATE = "delegate";
Julia Reynolds905df642019-05-31 15:29:59 -0400105 private static final String TAG_STATUS_ICONS = "silent_status_icons";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400106
107 private static final String ATT_VERSION = "version";
108 private static final String ATT_NAME = "name";
109 private static final String ATT_UID = "uid";
110 private static final String ATT_ID = "id";
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800111 private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400112 private static final String ATT_PRIORITY = "priority";
113 private static final String ATT_VISIBILITY = "visibility";
114 private static final String ATT_IMPORTANCE = "importance";
115 private static final String ATT_SHOW_BADGE = "show_badge";
116 private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400117 private static final String ATT_ENABLED = "enabled";
118 private static final String ATT_USER_ALLOWED = "allowed";
Julia Reynolds25692c42019-05-03 15:01:24 -0400119 private static final String ATT_HIDE_SILENT = "hide_gentle";
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400120 private static final String ATT_SENT_INVALID_MESSAGE = "sent_invalid_msg";
121 private static final String ATT_SENT_VALID_MESSAGE = "sent_valid_msg";
122 private static final String ATT_USER_DEMOTED_INVALID_MSG_APP = "user_demote_msg_app";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400123
124 private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
125 private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
126 private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500127 @VisibleForTesting
Julia Reynolds25692c42019-05-03 15:01:24 -0400128 static final boolean DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS = false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400129 private static final boolean DEFAULT_SHOW_BADGE = true;
Mady Mellora92268c2020-03-09 17:25:08 -0700130
Julia Reynolds413ba842019-01-11 10:38:08 -0500131 private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false;
Julia Reynolds0c245002019-03-27 16:10:11 -0400132 private static final boolean DEFAULT_APP_LOCKED_IMPORTANCE = false;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800133
Mady Mellora92268c2020-03-09 17:25:08 -0700134 static final boolean DEFAULT_GLOBAL_ALLOW_BUBBLE = true;
135 @VisibleForTesting
136 static final int DEFAULT_BUBBLE_PREFERENCE = BUBBLE_PREFERENCE_NONE;
137
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400138 /**
139 * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
140 * fields.
141 */
142 private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
Chris Wren1a934a32020-05-19 13:45:46 -0400143 private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400144
145 /**
146 * All user-lockable fields for a given application.
147 */
148 @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
149 public @interface LockableAppFields {
150 int USER_LOCKED_IMPORTANCE = 0x00000001;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800151 int USER_LOCKED_BUBBLE = 0x00000002;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400152 }
153
154 // pkg|uid => PackagePreferences
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400155 private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>();
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500156 // pkg|userId => PackagePreferences
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400157 private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
158
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400159 private final Context mContext;
160 private final PackageManager mPm;
161 private final RankingHandler mRankingHandler;
162 private final ZenModeHelper mZenModeHelper;
Will Brockman23db6d42020-02-28 09:51:12 -0500163 private final NotificationChannelLogger mNotificationChannelLogger;
Mady Mellorc888d512020-04-09 14:48:02 -0700164 private final AppOpsManager mAppOps;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400165
166 private SparseBooleanArray mBadgingEnabled;
Mady Mellora92268c2020-03-09 17:25:08 -0700167 private boolean mBubblesEnabledGlobally = DEFAULT_GLOBAL_ALLOW_BUBBLE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400168 private boolean mAreChannelsBypassingDnd;
Julia Reynolds2594b472019-04-03 13:30:16 -0400169 private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400170
Julia Reynolds7c267522020-01-16 11:26:41 -0500171 private boolean mAllowInvalidShortcuts = false;
172
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400173 public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
Mady Mellorc888d512020-04-09 14:48:02 -0700174 ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger,
Chris Wren1a934a32020-05-19 13:45:46 -0400175 AppOpsManager appOpsManager,
176 SysUiStatsEvent.BuilderFactory statsEventBuilderFactory) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400177 mContext = context;
178 mZenModeHelper = zenHelper;
179 mRankingHandler = rankingHandler;
180 mPm = pm;
Will Brockman23db6d42020-02-28 09:51:12 -0500181 mNotificationChannelLogger = notificationChannelLogger;
Mady Mellorc888d512020-04-09 14:48:02 -0700182 mAppOps = appOpsManager;
Chris Wren1a934a32020-05-19 13:45:46 -0400183 mStatsEventBuilderFactory = statsEventBuilderFactory;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400184
185 updateBadgingEnabled();
Julia Reynolds4509ce72019-01-31 13:12:43 -0500186 updateBubblesEnabled();
Beverly0479cde22018-11-09 11:05:34 -0500187 syncChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400188 }
189
Annie Meng8b646fd2019-02-01 18:46:42 +0000190 public void readXml(XmlPullParser parser, boolean forRestore, int userId)
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400191 throws XmlPullParserException, IOException {
192 int type = parser.getEventType();
193 if (type != XmlPullParser.START_TAG) return;
194 String tag = parser.getName();
195 if (!TAG_RANKING.equals(tag)) return;
Mady Mellorc888d512020-04-09 14:48:02 -0700196
197 boolean upgradeForBubbles = false;
198 if (parser.getAttributeCount() > 0) {
199 String attribute = parser.getAttributeName(0);
200 if (ATT_VERSION.equals(attribute)) {
201 int xmlVersion = Integer.parseInt(parser.getAttributeValue(0));
202 upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
203 }
204 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400205 synchronized (mPackagePreferences) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400206 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
207 tag = parser.getName();
208 if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
209 return;
210 }
211 if (type == XmlPullParser.START_TAG) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500212 if (TAG_STATUS_ICONS.equals(tag)) {
Annie Meng8b646fd2019-02-01 18:46:42 +0000213 if (forRestore && userId != UserHandle.USER_SYSTEM) {
214 continue;
215 }
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500216 mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute(
217 parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
218 } else if (TAG_PACKAGE.equals(tag)) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400219 int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
220 String name = parser.getAttributeValue(null, ATT_NAME);
221 if (!TextUtils.isEmpty(name)) {
222 if (forRestore) {
223 try {
Annie Meng8b646fd2019-02-01 18:46:42 +0000224 uid = mPm.getPackageUidAsUser(name, userId);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400225 } catch (PackageManager.NameNotFoundException e) {
226 // noop
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400227 }
228 }
Julia Reynoldsc29370a2019-08-20 16:08:42 -0400229 boolean skipWarningLogged = false;
Mady Mellorc888d512020-04-09 14:48:02 -0700230 boolean hasSAWPermission = false;
Mady Mellorbccdf452020-05-27 11:57:12 -0700231 if (upgradeForBubbles && uid != UNKNOWN_UID) {
Mady Mellorc888d512020-04-09 14:48:02 -0700232 hasSAWPermission = mAppOps.noteOpNoThrow(
233 OP_SYSTEM_ALERT_WINDOW, uid, name, null,
234 "check-notif-bubble") == AppOpsManager.MODE_ALLOWED;
235 }
236 int bubblePref = hasSAWPermission
237 ? BUBBLE_PREFERENCE_ALL
238 : XmlUtils.readIntAttribute(parser, ATT_ALLOW_BUBBLE,
239 DEFAULT_BUBBLE_PREFERENCE);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400240
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500241 PackagePreferences r = getOrCreatePackagePreferencesLocked(
242 name, userId, uid,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400243 XmlUtils.readIntAttribute(
244 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
245 XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
246 DEFAULT_PRIORITY),
247 XmlUtils.readIntAttribute(
248 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
249 XmlUtils.readBooleanAttribute(
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500250 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
Mady Mellorc888d512020-04-09 14:48:02 -0700251 bubblePref);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400252 r.importance = XmlUtils.readIntAttribute(
253 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
254 r.priority = XmlUtils.readIntAttribute(
255 parser, ATT_PRIORITY, DEFAULT_PRIORITY);
256 r.visibility = XmlUtils.readIntAttribute(
257 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
258 r.showBadge = XmlUtils.readBooleanAttribute(
259 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
260 r.lockedAppFields = XmlUtils.readIntAttribute(parser,
261 ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400262 r.hasSentInvalidMessage = XmlUtils.readBooleanAttribute(
263 parser, ATT_SENT_INVALID_MESSAGE, false);
264 r.hasSentValidMessage = XmlUtils.readBooleanAttribute(
265 parser, ATT_SENT_VALID_MESSAGE, false);
266 r.userDemotedMsgApp = XmlUtils.readBooleanAttribute(
267 parser, ATT_USER_DEMOTED_INVALID_MSG_APP, false);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400268
269 final int innerDepth = parser.getDepth();
270 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
271 && (type != XmlPullParser.END_TAG
272 || parser.getDepth() > innerDepth)) {
273 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
274 continue;
275 }
276
277 String tagName = parser.getName();
278 // Channel groups
279 if (TAG_GROUP.equals(tagName)) {
280 String id = parser.getAttributeValue(null, ATT_ID);
281 CharSequence groupName = parser.getAttributeValue(null,
282 ATT_NAME);
283 if (!TextUtils.isEmpty(id)) {
284 NotificationChannelGroup group
285 = new NotificationChannelGroup(id, groupName);
286 group.populateFromXml(parser);
287 r.groups.put(id, group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400288 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400289 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400290 // Channels
291 if (TAG_CHANNEL.equals(tagName)) {
Julia Reynoldsc29370a2019-08-20 16:08:42 -0400292 if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
293 if (!skipWarningLogged) {
294 Slog.w(TAG, "Skipping further channels for " + r.pkg
295 + "; app has too many");
296 skipWarningLogged = true;
297 }
298 continue;
299 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400300 String id = parser.getAttributeValue(null, ATT_ID);
301 String channelName = parser.getAttributeValue(null, ATT_NAME);
302 int channelImportance = XmlUtils.readIntAttribute(
303 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
304 if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
305 NotificationChannel channel = new NotificationChannel(id,
306 channelName, channelImportance);
307 if (forRestore) {
308 channel.populateFromXmlForRestore(parser, mContext);
309 } else {
310 channel.populateFromXml(parser);
311 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -0400312 channel.setImportanceLockedByCriticalDeviceFunction(
313 r.defaultAppLockedImportance);
Julia Reynolds7c267522020-01-16 11:26:41 -0500314 boolean isInvalidShortcutChannel =
315 channel.getConversationId() != null &&
316 channel.getConversationId().contains(
317 PLACEHOLDER_CONVERSATION_ID);
318 if (mAllowInvalidShortcuts || (!mAllowInvalidShortcuts
319 && !isInvalidShortcutChannel)) {
320 r.channels.put(id, channel);
321 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400322 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400323 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400324 // Delegate
325 if (TAG_DELEGATE.equals(tagName)) {
326 int delegateId =
327 XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
328 String delegateName =
329 XmlUtils.readStringAttribute(parser, ATT_NAME);
330 boolean delegateEnabled = XmlUtils.readBooleanAttribute(
331 parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
332 boolean userAllowed = XmlUtils.readBooleanAttribute(
333 parser, ATT_USER_ALLOWED,
334 Delegate.DEFAULT_USER_ALLOWED);
335 Delegate d = null;
336 if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(
337 delegateName)) {
338 d = new Delegate(
339 delegateName, delegateId, delegateEnabled,
340 userAllowed);
341 }
342 r.delegate = d;
343 }
344
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400345 }
346
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400347 try {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400348 deleteDefaultChannelIfNeededLocked(r);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400349 } catch (PackageManager.NameNotFoundException e) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400350 Slog.e(TAG, "deleteDefaultChannelIfNeededLocked - Exception: " + e);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400351 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400352 }
353 }
354 }
355 }
356 }
357 throw new IllegalStateException("Failed to reach END_DOCUMENT");
358 }
359
Julia Reynolds5c399c62019-04-08 14:42:53 -0400360 private PackagePreferences getPackagePreferencesLocked(String pkg, int uid) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400361 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400362 return mPackagePreferences.get(key);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400363 }
364
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500365 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
366 int uid) {
367 return getOrCreatePackagePreferencesLocked(pkg, UserHandle.getUserId(uid), uid,
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500368 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
Mady Mellora92268c2020-03-09 17:25:08 -0700369 DEFAULT_BUBBLE_PREFERENCE);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400370 }
371
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500372 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
373 @UserIdInt int userId, int uid) {
374 return getOrCreatePackagePreferencesLocked(pkg, userId, uid,
375 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
Mady Mellora92268c2020-03-09 17:25:08 -0700376 DEFAULT_BUBBLE_PREFERENCE);
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500377 }
378
379 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
380 @UserIdInt int userId, int uid, int importance, int priority, int visibility,
Mady Mellora92268c2020-03-09 17:25:08 -0700381 boolean showBadge, int bubblePreference) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400382 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400383 PackagePreferences
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500384 r = (uid == UNKNOWN_UID)
385 ? mRestoredWithoutUids.get(unrestoredPackageKey(pkg, userId))
Julia Reynolds5c399c62019-04-08 14:42:53 -0400386 : mPackagePreferences.get(key);
387 if (r == null) {
388 r = new PackagePreferences();
389 r.pkg = pkg;
390 r.uid = uid;
391 r.importance = importance;
392 r.priority = priority;
393 r.visibility = visibility;
394 r.showBadge = showBadge;
Mady Mellora92268c2020-03-09 17:25:08 -0700395 r.bubblePreference = bubblePreference;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400396
Julia Reynolds5c399c62019-04-08 14:42:53 -0400397 try {
398 createDefaultChannelIfNeededLocked(r);
399 } catch (PackageManager.NameNotFoundException e) {
400 Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400401 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400402
403 if (r.uid == UNKNOWN_UID) {
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500404 mRestoredWithoutUids.put(unrestoredPackageKey(pkg, userId), r);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400405 } else {
406 mPackagePreferences.put(key, r);
407 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400408 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400409 return r;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400410 }
411
412 private boolean shouldHaveDefaultChannel(PackagePreferences r) throws
413 PackageManager.NameNotFoundException {
414 final int userId = UserHandle.getUserId(r.uid);
415 final ApplicationInfo applicationInfo =
416 mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
417 if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
418 // O apps should not have the default channel.
419 return false;
420 }
421
422 // Otherwise, this app should have the default channel.
423 return true;
424 }
425
Julia Reynolds996c7c12019-05-24 10:25:33 -0400426 private boolean deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400427 PackageManager.NameNotFoundException {
428 if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
429 // Not present
Julia Reynolds996c7c12019-05-24 10:25:33 -0400430 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400431 }
432
433 if (shouldHaveDefaultChannel(r)) {
434 // Keep the default channel until upgraded.
Julia Reynolds996c7c12019-05-24 10:25:33 -0400435 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400436 }
437
438 // Remove Default Channel.
439 r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
Julia Reynolds996c7c12019-05-24 10:25:33 -0400440
441 return true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400442 }
443
Julia Reynolds996c7c12019-05-24 10:25:33 -0400444 private boolean createDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400445 PackageManager.NameNotFoundException {
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500446 if (r.uid == UNKNOWN_UID) {
447 return false;
448 }
449
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400450 if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
451 r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
452 com.android.internal.R.string.default_notification_channel_label));
Julia Reynolds996c7c12019-05-24 10:25:33 -0400453 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400454 }
455
456 if (!shouldHaveDefaultChannel(r)) {
457 // Keep the default channel until upgraded.
Julia Reynolds996c7c12019-05-24 10:25:33 -0400458 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400459 }
460
461 // Create Default Channel
462 NotificationChannel channel;
463 channel = new NotificationChannel(
464 NotificationChannel.DEFAULT_CHANNEL_ID,
465 mContext.getString(R.string.default_notification_channel_label),
466 r.importance);
467 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
468 channel.setLockscreenVisibility(r.visibility);
469 if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
470 channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
471 }
472 if (r.priority != DEFAULT_PRIORITY) {
473 channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
474 }
475 if (r.visibility != DEFAULT_VISIBILITY) {
476 channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
477 }
478 r.channels.put(channel.getId(), channel);
Julia Reynolds996c7c12019-05-24 10:25:33 -0400479
480 return true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400481 }
482
Annie Meng8b646fd2019-02-01 18:46:42 +0000483 public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400484 out.startTag(null, TAG_RANKING);
485 out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
Annie Meng8b646fd2019-02-01 18:46:42 +0000486 if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS
487 && (!forBackup || userId == UserHandle.USER_SYSTEM)) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500488 out.startTag(null, TAG_STATUS_ICONS);
489 out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons));
490 out.endTag(null, TAG_STATUS_ICONS);
491 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400492
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400493 synchronized (mPackagePreferences) {
494 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400495 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400496 final PackagePreferences r = mPackagePreferences.valueAt(i);
Annie Meng8b646fd2019-02-01 18:46:42 +0000497 if (forBackup && UserHandle.getUserId(r.uid) != userId) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400498 continue;
499 }
500 final boolean hasNonDefaultSettings =
501 r.importance != DEFAULT_IMPORTANCE
502 || r.priority != DEFAULT_PRIORITY
503 || r.visibility != DEFAULT_VISIBILITY
504 || r.showBadge != DEFAULT_SHOW_BADGE
505 || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
506 || r.channels.size() > 0
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400507 || r.groups.size() > 0
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500508 || r.delegate != null
Julia Reynoldsa7dac432020-04-23 12:17:31 -0400509 || r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400510 || r.hasSentInvalidMessage
511 || r.userDemotedMsgApp
512 || r.hasSentValidMessage;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400513 if (hasNonDefaultSettings) {
514 out.startTag(null, TAG_PACKAGE);
515 out.attribute(null, ATT_NAME, r.pkg);
516 if (r.importance != DEFAULT_IMPORTANCE) {
517 out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
518 }
519 if (r.priority != DEFAULT_PRIORITY) {
520 out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
521 }
522 if (r.visibility != DEFAULT_VISIBILITY) {
523 out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
524 }
Mady Mellora92268c2020-03-09 17:25:08 -0700525 if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) {
526 out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(r.bubblePreference));
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500527 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400528 out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
529 out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
530 Integer.toString(r.lockedAppFields));
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400531 out.attribute(null, ATT_SENT_INVALID_MESSAGE,
532 Boolean.toString(r.hasSentInvalidMessage));
533 out.attribute(null, ATT_SENT_VALID_MESSAGE,
534 Boolean.toString(r.hasSentValidMessage));
535 out.attribute(null, ATT_USER_DEMOTED_INVALID_MSG_APP,
536 Boolean.toString(r.userDemotedMsgApp));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400537
538 if (!forBackup) {
539 out.attribute(null, ATT_UID, Integer.toString(r.uid));
540 }
541
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400542 if (r.delegate != null) {
543 out.startTag(null, TAG_DELEGATE);
544
545 out.attribute(null, ATT_NAME, r.delegate.mPkg);
546 out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
547 if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
548 out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
549 }
550 if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
551 out.attribute(null, ATT_USER_ALLOWED,
552 Boolean.toString(r.delegate.mUserAllowed));
553 }
554 out.endTag(null, TAG_DELEGATE);
555 }
556
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400557 for (NotificationChannelGroup group : r.groups.values()) {
558 group.writeXml(out);
559 }
560
561 for (NotificationChannel channel : r.channels.values()) {
562 if (forBackup) {
563 if (!channel.isDeleted()) {
564 channel.writeXmlForBackup(out, mContext);
565 }
566 } else {
567 channel.writeXml(out);
568 }
569 }
570
571 out.endTag(null, TAG_PACKAGE);
572 }
573 }
574 }
575 out.endTag(null, TAG_RANKING);
576 }
577
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800578 /**
579 * Sets whether bubbles are allowed.
580 *
581 * @param pkg the package to allow or not allow bubbles for.
582 * @param uid the uid to allow or not allow bubbles for.
Mady Mellora92268c2020-03-09 17:25:08 -0700583 * @param bubblePreference whether bubbles are allowed.
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800584 */
Mady Mellora92268c2020-03-09 17:25:08 -0700585 public void setBubblesAllowed(String pkg, int uid, int bubblePreference) {
Mady Mellor9f296142019-05-24 09:42:52 -0700586 boolean changed = false;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400587 synchronized (mPackagePreferences) {
588 PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid);
Mady Mellora92268c2020-03-09 17:25:08 -0700589 changed = p.bubblePreference != bubblePreference;
590 p.bubblePreference = bubblePreference;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400591 p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
592 }
Mady Mellor9f296142019-05-24 09:42:52 -0700593 if (changed) {
594 updateConfig();
595 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500596 }
597
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800598 /**
599 * Whether bubbles are allowed.
600 *
601 * @param pkg the package to check if bubbles are allowed for
602 * @param uid the uid to check if bubbles are allowed for.
603 * @return whether bubbles are allowed.
604 */
Julia Reynolds4509ce72019-01-31 13:12:43 -0500605 @Override
Mady Mellora92268c2020-03-09 17:25:08 -0700606 public int getBubblePreference(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400607 synchronized (mPackagePreferences) {
Mady Mellora92268c2020-03-09 17:25:08 -0700608 return getOrCreatePackagePreferencesLocked(pkg, uid).bubblePreference;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400609 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500610 }
611
612 public int getAppLockedFields(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400613 synchronized (mPackagePreferences) {
614 return getOrCreatePackagePreferencesLocked(pkg, uid).lockedAppFields;
615 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500616 }
617
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400618 /**
619 * Gets importance.
620 */
621 @Override
622 public int getImportance(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400623 synchronized (mPackagePreferences) {
624 return getOrCreatePackagePreferencesLocked(packageName, uid).importance;
625 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400626 }
627
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400628 /**
629 * Returns whether the importance of the corresponding notification is user-locked and shouldn't
630 * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
631 * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
632 */
633 public boolean getIsAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400634 synchronized (mPackagePreferences) {
635 int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields;
636 return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
637 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400638 }
639
640 @Override
641 public boolean canShowBadge(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400642 synchronized (mPackagePreferences) {
643 return getOrCreatePackagePreferencesLocked(packageName, uid).showBadge;
644 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400645 }
646
647 @Override
648 public void setShowBadge(String packageName, int uid, boolean showBadge) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400649 synchronized (mPackagePreferences) {
650 getOrCreatePackagePreferencesLocked(packageName, uid).showBadge = showBadge;
651 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400652 updateConfig();
653 }
654
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400655 public boolean isInInvalidMsgState(String packageName, int uid) {
Julia Reynoldsa7dac432020-04-23 12:17:31 -0400656 synchronized (mPackagePreferences) {
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400657 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
658 return r.hasSentInvalidMessage && !r.hasSentValidMessage;
Julia Reynoldsa7dac432020-04-23 12:17:31 -0400659 }
660 }
661
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400662 public boolean hasUserDemotedInvalidMsgApp(String packageName, int uid) {
Julia Reynoldsa7dac432020-04-23 12:17:31 -0400663 synchronized (mPackagePreferences) {
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400664 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
665 return isInInvalidMsgState(packageName, uid) ? r.userDemotedMsgApp : false;
666 }
667 }
668
669 public void setInvalidMsgAppDemoted(String packageName, int uid, boolean isDemoted) {
670 synchronized (mPackagePreferences) {
671 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
672 r.userDemotedMsgApp = isDemoted;
673 }
674 }
675
676 public boolean setInvalidMessageSent(String packageName, int uid) {
677 synchronized (mPackagePreferences) {
678 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
679 boolean valueChanged = r.hasSentInvalidMessage == false;
680 r.hasSentInvalidMessage = true;
681
682 return valueChanged;
683 }
684 }
685
686 public boolean setValidMessageSent(String packageName, int uid) {
687 synchronized (mPackagePreferences) {
688 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
689 boolean valueChanged = r.hasSentValidMessage == false;
690 r.hasSentValidMessage = true;
691
692 return valueChanged;
693 }
694 }
695
696 @VisibleForTesting
697 boolean hasSentInvalidMsg(String packageName, int uid) {
698 synchronized (mPackagePreferences) {
699 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
700 return r.hasSentInvalidMessage;
701 }
702 }
703
704 @VisibleForTesting
705 boolean hasSentValidMsg(String packageName, int uid) {
706 synchronized (mPackagePreferences) {
707 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
708 return r.hasSentValidMessage;
709 }
710 }
711
712 @VisibleForTesting
713 boolean didUserEverDemoteInvalidMsgApp(String packageName, int uid) {
714 synchronized (mPackagePreferences) {
715 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
716 return r.userDemotedMsgApp;
Julia Reynoldsa7dac432020-04-23 12:17:31 -0400717 }
718 }
719
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400720 @Override
721 public boolean isGroupBlocked(String packageName, int uid, String groupId) {
722 if (groupId == null) {
723 return false;
724 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400725 synchronized (mPackagePreferences) {
726 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
727 NotificationChannelGroup group = r.groups.get(groupId);
728 if (group == null) {
729 return false;
730 }
731 return group.isBlocked();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400732 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400733 }
734
735 int getPackagePriority(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400736 synchronized (mPackagePreferences) {
737 return getOrCreatePackagePreferencesLocked(pkg, uid).priority;
738 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400739 }
740
741 int getPackageVisibility(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400742 synchronized (mPackagePreferences) {
743 return getOrCreatePackagePreferencesLocked(pkg, uid).visibility;
744 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400745 }
746
747 @Override
748 public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
749 boolean fromTargetApp) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +0000750 Objects.requireNonNull(pkg);
751 Objects.requireNonNull(group);
752 Objects.requireNonNull(group.getId());
753 Objects.requireNonNull(!TextUtils.isEmpty(group.getName()));
Julia Reynolds5c399c62019-04-08 14:42:53 -0400754 synchronized (mPackagePreferences) {
755 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
756 if (r == null) {
757 throw new IllegalArgumentException("Invalid package");
758 }
759 final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400760 if (oldGroup != null) {
761 group.setChannels(oldGroup.getChannels());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400762
Julia Reynolds5c399c62019-04-08 14:42:53 -0400763 // apps can't update the blocked status or app overlay permission
764 if (fromTargetApp) {
765 group.setBlocked(oldGroup.isBlocked());
766 group.unlockFields(group.getUserLockedFields());
767 group.lockFields(oldGroup.getUserLockedFields());
768 } else {
769 // but the system can
770 if (group.isBlocked() != oldGroup.isBlocked()) {
771 group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
772 updateChannelsBypassingDnd(mContext.getUserId());
773 }
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -0400774 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400775 }
Will Brockman23db6d42020-02-28 09:51:12 -0500776 if (!group.equals(oldGroup)) {
777 // will log for new entries as well as name/description changes
778 MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
779 mNotificationChannelLogger.logNotificationChannelGroup(group, uid, pkg,
780 oldGroup == null,
781 (oldGroup != null) && oldGroup.isBlocked());
782 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400783 r.groups.put(group.getId(), group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400784 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400785 }
786
787 @Override
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400788 public boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400789 boolean fromTargetApp, boolean hasDndAccess) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +0000790 Objects.requireNonNull(pkg);
791 Objects.requireNonNull(channel);
792 Objects.requireNonNull(channel.getId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400793 Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
Will Brockman23db6d42020-02-28 09:51:12 -0500794 boolean needsPolicyFileChange = false, wasUndeleted = false;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400795 synchronized (mPackagePreferences) {
796 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
797 if (r == null) {
798 throw new IllegalArgumentException("Invalid package");
799 }
800 if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
801 throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
802 }
803 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
804 throw new IllegalArgumentException("Reserved id");
805 }
806 NotificationChannel existing = r.channels.get(channel.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400807 if (existing != null && fromTargetApp) {
Will Brockman23db6d42020-02-28 09:51:12 -0500808 // Actually modifying an existing channel - keep most of the existing settings
Julia Reynolds5c399c62019-04-08 14:42:53 -0400809 if (existing.isDeleted()) {
Will Brockman23db6d42020-02-28 09:51:12 -0500810 // The existing channel was deleted - undelete it.
Julia Reynolds5c399c62019-04-08 14:42:53 -0400811 existing.setDeleted(false);
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400812 needsPolicyFileChange = true;
Will Brockman23db6d42020-02-28 09:51:12 -0500813 wasUndeleted = true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400814
Julia Reynolds5c399c62019-04-08 14:42:53 -0400815 // log a resurrected channel as if it's new again
816 MetricsLogger.action(getChannelLog(channel, pkg).setType(
817 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
Will Brockman23db6d42020-02-28 09:51:12 -0500818 mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400819 }
820
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400821 if (!Objects.equals(channel.getName().toString(), existing.getName().toString())) {
822 existing.setName(channel.getName().toString());
823 needsPolicyFileChange = true;
824 }
825 if (!Objects.equals(channel.getDescription(), existing.getDescription())) {
826 existing.setDescription(channel.getDescription());
827 needsPolicyFileChange = true;
828 }
Hall Liu9866aa82020-03-12 12:55:50 -0700829 if (channel.isBlockable() != existing.isBlockable()) {
830 existing.setBlockable(channel.isBlockable());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400831 needsPolicyFileChange = true;
832 }
833 if (channel.getGroup() != null && existing.getGroup() == null) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400834 existing.setGroup(channel.getGroup());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400835 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400836 }
837
838 // Apps are allowed to downgrade channel importance if the user has not changed any
839 // fields on this channel yet.
840 final int previousExistingImportance = existing.getImportance();
Will Brockman3ec09342020-06-19 09:11:06 -0400841 final int previousLoggingImportance =
842 NotificationChannelLogger.getLoggingImportance(existing);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400843 if (existing.getUserLockedFields() == 0 &&
844 channel.getImportance() < existing.getImportance()) {
845 existing.setImportance(channel.getImportance());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400846 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400847 }
848
849 // system apps and dnd access apps can bypass dnd if the user hasn't changed any
850 // fields on the channel yet
851 if (existing.getUserLockedFields() == 0 && hasDndAccess) {
852 boolean bypassDnd = channel.canBypassDnd();
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400853 if (bypassDnd != existing.canBypassDnd()) {
854 existing.setBypassDnd(bypassDnd);
855 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400856
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400857 if (bypassDnd != mAreChannelsBypassingDnd
858 || previousExistingImportance != existing.getImportance()) {
859 updateChannelsBypassingDnd(mContext.getUserId());
860 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400861 }
862 }
863
Julia Reynolds469144c2019-06-21 14:30:28 -0400864 if (existing.getOriginalImportance() == IMPORTANCE_UNSPECIFIED) {
865 existing.setOriginalImportance(channel.getImportance());
866 needsPolicyFileChange = true;
867 }
868
Julia Reynolds5c399c62019-04-08 14:42:53 -0400869 updateConfig();
Will Brockman23db6d42020-02-28 09:51:12 -0500870 if (needsPolicyFileChange && !wasUndeleted) {
871 mNotificationChannelLogger.logNotificationChannelModified(existing, uid, pkg,
Will Brockman3ec09342020-06-19 09:11:06 -0400872 previousLoggingImportance, false);
Will Brockman23db6d42020-02-28 09:51:12 -0500873 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400874 return needsPolicyFileChange;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400875 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400876
Julia Reynoldsc29370a2019-08-20 16:08:42 -0400877 if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
878 throw new IllegalStateException("Limit exceed; cannot create more channels");
879 }
880
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400881 needsPolicyFileChange = true;
882
Julia Reynolds5c399c62019-04-08 14:42:53 -0400883 if (channel.getImportance() < IMPORTANCE_NONE
884 || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
885 throw new IllegalArgumentException("Invalid importance level");
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400886 }
887
Julia Reynolds5c399c62019-04-08 14:42:53 -0400888 // Reset fields that apps aren't allowed to set.
889 if (fromTargetApp && !hasDndAccess) {
890 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400891 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400892 if (fromTargetApp) {
893 channel.setLockscreenVisibility(r.visibility);
Mady Melloree8d5b59e2020-04-09 14:19:53 -0700894 channel.setAllowBubbles(existing != null
895 ? existing.getAllowBubbles()
896 : NotificationChannel.DEFAULT_ALLOW_BUBBLE);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400897 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400898 clearLockedFieldsLocked(channel);
899 channel.setImportanceLockedByOEM(r.oemLockedImportance);
900 if (!channel.isImportanceLockedByOEM()) {
Julia Reynolds72b28442019-11-12 11:43:39 -0500901 if (r.oemLockedChannels.contains(channel.getId())) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400902 channel.setImportanceLockedByOEM(true);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400903 }
904 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400905 channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
906 if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
907 channel.setLockscreenVisibility(
908 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
Julia Reynolds413ba842019-01-11 10:38:08 -0500909 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400910 if (!r.showBadge) {
911 channel.setShowBadge(false);
912 }
Julia Reynolds469144c2019-06-21 14:30:28 -0400913 channel.setOriginalImportance(channel.getImportance());
Julia Reynolds0f767342019-12-18 09:11:55 -0500914
915 // validate parent
916 if (channel.getParentChannelId() != null) {
917 Preconditions.checkArgument(r.channels.containsKey(channel.getParentChannelId()),
918 "Tried to create a conversation channel without a preexisting parent");
919 }
920
Julia Reynolds5c399c62019-04-08 14:42:53 -0400921 r.channels.put(channel.getId(), channel);
922 if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
923 updateChannelsBypassingDnd(mContext.getUserId());
924 }
925 MetricsLogger.action(getChannelLog(channel, pkg).setType(
926 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
Will Brockman23db6d42020-02-28 09:51:12 -0500927 mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400928 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400929
930 return needsPolicyFileChange;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400931 }
932
Julia Reynolds5c399c62019-04-08 14:42:53 -0400933 void clearLockedFieldsLocked(NotificationChannel channel) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400934 channel.unlockFields(channel.getUserLockedFields());
935 }
936
937 @Override
938 public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
939 boolean fromUser) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +0000940 Objects.requireNonNull(updatedChannel);
941 Objects.requireNonNull(updatedChannel.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400942 synchronized (mPackagePreferences) {
943 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
944 if (r == null) {
945 throw new IllegalArgumentException("Invalid package");
946 }
947 NotificationChannel channel = r.channels.get(updatedChannel.getId());
948 if (channel == null || channel.isDeleted()) {
949 throw new IllegalArgumentException("Channel does not exist");
950 }
951 if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
952 updatedChannel.setLockscreenVisibility(
953 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
954 }
955 if (fromUser) {
956 updatedChannel.lockFields(channel.getUserLockedFields());
957 lockFieldsForUpdateLocked(channel, updatedChannel);
958 } else {
959 updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
960 }
961 // no importance updates are allowed if OEM blocked it
962 updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
963 if (updatedChannel.isImportanceLockedByOEM()) {
964 updatedChannel.setImportance(channel.getImportance());
965 }
966 updatedChannel.setImportanceLockedByCriticalDeviceFunction(
967 r.defaultAppLockedImportance);
Beverly47679222019-05-16 15:46:11 -0400968 if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
969 && updatedChannel.getImportance() == IMPORTANCE_NONE) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400970 updatedChannel.setImportance(channel.getImportance());
971 }
Julia Reynolds413ba842019-01-11 10:38:08 -0500972
Julia Reynolds5c399c62019-04-08 14:42:53 -0400973 r.channels.put(updatedChannel.getId(), updatedChannel);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400974
Julia Reynolds5c399c62019-04-08 14:42:53 -0400975 if (onlyHasDefaultChannel(pkg, uid)) {
976 // copy settings to app level so they are inherited by new channels
977 // when the app migrates
978 r.importance = updatedChannel.getImportance();
979 r.priority = updatedChannel.canBypassDnd()
980 ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
981 r.visibility = updatedChannel.getLockscreenVisibility();
982 r.showBadge = updatedChannel.canShowBadge();
983 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400984
Julia Reynolds5c399c62019-04-08 14:42:53 -0400985 if (!channel.equals(updatedChannel)) {
986 // only log if there are real changes
987 MetricsLogger.action(getChannelLog(updatedChannel, pkg)
988 .setSubtype(fromUser ? 1 : 0));
Will Brockman23db6d42020-02-28 09:51:12 -0500989 mNotificationChannelLogger.logNotificationChannelModified(updatedChannel, uid, pkg,
Will Brockman3ec09342020-06-19 09:11:06 -0400990 NotificationChannelLogger.getLoggingImportance(channel), fromUser);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400991 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400992
Julia Reynolds5c399c62019-04-08 14:42:53 -0400993 if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
994 || channel.getImportance() != updatedChannel.getImportance()) {
995 updateChannelsBypassingDnd(mContext.getUserId());
996 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400997 }
998 updateConfig();
999 }
1000
1001 @Override
1002 public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
1003 boolean includeDeleted) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001004 Objects.requireNonNull(pkg);
Julia Reynolds12ba4cf2020-01-10 16:01:38 -05001005 return getConversationNotificationChannel(pkg, uid, channelId, null, true, includeDeleted);
Julia Reynolds0f767342019-12-18 09:11:55 -05001006 }
1007
1008 @Override
Julia Reynolds12ba4cf2020-01-10 16:01:38 -05001009 public NotificationChannel getConversationNotificationChannel(String pkg, int uid,
1010 String channelId, String conversationId, boolean returnParentIfNoConversationChannel,
1011 boolean includeDeleted) {
Julia Reynolds0f767342019-12-18 09:11:55 -05001012 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001013 synchronized (mPackagePreferences) {
1014 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1015 if (r == null) {
1016 return null;
1017 }
1018 if (channelId == null) {
1019 channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
1020 }
Julia Reynolds12ba4cf2020-01-10 16:01:38 -05001021 NotificationChannel channel = null;
1022 if (conversationId != null) {
1023 // look for an automatically created conversation specific channel
1024 channel = findConversationChannel(r, channelId, conversationId, includeDeleted);
1025 }
1026 if (channel == null && returnParentIfNoConversationChannel) {
1027 // look for it just based on its id
Julia Reynolds0f767342019-12-18 09:11:55 -05001028 final NotificationChannel nc = r.channels.get(channelId);
1029 if (nc != null && (includeDeleted || !nc.isDeleted())) {
1030 return nc;
1031 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001032 }
Julia Reynolds12ba4cf2020-01-10 16:01:38 -05001033 return channel;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001034 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001035 }
1036
Julia Reynolds0f767342019-12-18 09:11:55 -05001037 private NotificationChannel findConversationChannel(PackagePreferences p, String parentId,
1038 String conversationId, boolean includeDeleted) {
1039 for (NotificationChannel nc : p.channels.values()) {
1040 if (conversationId.equals(nc.getConversationId())
1041 && parentId.equals(nc.getParentChannelId())
1042 && (includeDeleted || !nc.isDeleted())) {
1043 return nc;
1044 }
1045 }
1046 return null;
1047 }
1048
1049 public List<NotificationChannel> getNotificationChannelsByConversationId(String pkg, int uid,
1050 String conversationId) {
1051 Preconditions.checkNotNull(pkg);
1052 Preconditions.checkNotNull(conversationId);
1053 List<NotificationChannel> channels = new ArrayList<>();
1054 synchronized (mPackagePreferences) {
1055 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1056 if (r == null) {
1057 return channels;
1058 }
1059 for (NotificationChannel nc : r.channels.values()) {
1060 if (conversationId.equals(nc.getConversationId())
1061 && !nc.isDeleted()) {
1062 channels.add(nc);
1063 }
1064 }
1065 return channels;
1066 }
1067 }
1068
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001069 @Override
1070 public void deleteNotificationChannel(String pkg, int uid, String channelId) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001071 synchronized (mPackagePreferences) {
1072 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1073 if (r == null) {
1074 return;
1075 }
1076 NotificationChannel channel = r.channels.get(channelId);
1077 if (channel != null) {
Will Brockman23db6d42020-02-28 09:51:12 -05001078 deleteNotificationChannelLocked(channel, pkg, uid);
1079 }
1080 }
1081 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001082
Will Brockman23db6d42020-02-28 09:51:12 -05001083 private void deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
1084 if (!channel.isDeleted()) {
1085 channel.setDeleted(true);
1086 LogMaker lm = getChannelLog(channel, pkg);
1087 lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
1088 MetricsLogger.action(lm);
1089 mNotificationChannelLogger.logNotificationChannelDeleted(channel, uid, pkg);
1090
1091 if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
1092 updateChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001093 }
1094 }
1095 }
1096
1097 @Override
1098 @VisibleForTesting
1099 public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001100 Objects.requireNonNull(pkg);
1101 Objects.requireNonNull(channelId);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001102 synchronized (mPackagePreferences) {
1103 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1104 if (r == null) {
1105 return;
1106 }
1107 r.channels.remove(channelId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001108 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001109 }
1110
1111 @Override
1112 public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001113 Objects.requireNonNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001114 synchronized (mPackagePreferences) {
1115 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1116 if (r == null) {
1117 return;
1118 }
1119 int N = r.channels.size() - 1;
1120 for (int i = N; i >= 0; i--) {
1121 String key = r.channels.keyAt(i);
1122 if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
1123 r.channels.remove(key);
1124 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001125 }
1126 }
1127 }
1128
Julia Reynolds12ad7ca2019-01-28 09:29:16 -05001129 public boolean shouldHideSilentStatusIcons() {
1130 return mHideSilentStatusBarIcons;
1131 }
1132
1133 public void setHideSilentStatusIcons(boolean hide) {
1134 mHideSilentStatusBarIcons = hide;
1135 }
1136
Julia Reynolds413ba842019-01-11 10:38:08 -05001137 public void lockChannelsForOEM(String[] appOrChannelList) {
1138 if (appOrChannelList == null) {
1139 return;
1140 }
1141 for (String appOrChannel : appOrChannelList) {
1142 if (!TextUtils.isEmpty(appOrChannel)) {
1143 String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
1144 if (appSplit != null && appSplit.length > 0) {
1145 String appName = appSplit[0];
1146 String channelId = appSplit.length == 2 ? appSplit[1] : null;
1147
1148 synchronized (mPackagePreferences) {
1149 for (PackagePreferences r : mPackagePreferences.values()) {
1150 if (r.pkg.equals(appName)) {
1151 if (channelId == null) {
1152 // lock all channels for the app
1153 r.oemLockedImportance = true;
1154 for (NotificationChannel channel : r.channels.values()) {
1155 channel.setImportanceLockedByOEM(true);
1156 }
1157 } else {
1158 NotificationChannel channel = r.channels.get(channelId);
1159 if (channel != null) {
1160 channel.setImportanceLockedByOEM(true);
Julia Reynolds413ba842019-01-11 10:38:08 -05001161 }
Julia Reynolds72b28442019-11-12 11:43:39 -05001162 // Also store the locked channels on the record, so they aren't
1163 // temporarily lost when data is cleared on the package
1164 r.oemLockedChannels.add(channelId);
Julia Reynolds413ba842019-01-11 10:38:08 -05001165 }
1166 }
1167 }
1168 }
1169 }
1170 }
1171 }
1172 }
1173
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001174 public void updateDefaultApps(int userId, ArraySet<String> toRemove,
1175 ArraySet<Pair<String, Integer>> toAdd) {
Julia Reynolds0c245002019-03-27 16:10:11 -04001176 synchronized (mPackagePreferences) {
1177 for (PackagePreferences p : mPackagePreferences.values()) {
1178 if (userId == UserHandle.getUserId(p.uid)) {
1179 if (toRemove != null && toRemove.contains(p.pkg)) {
1180 p.defaultAppLockedImportance = false;
1181 for (NotificationChannel channel : p.channels.values()) {
1182 channel.setImportanceLockedByCriticalDeviceFunction(false);
1183 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001184 }
1185 }
1186 }
1187 if (toAdd != null) {
1188 for (Pair<String, Integer> approvedApp : toAdd) {
1189 PackagePreferences p = getOrCreatePackagePreferencesLocked(approvedApp.first,
1190 approvedApp.second);
1191 p.defaultAppLockedImportance = true;
1192 for (NotificationChannel channel : p.channels.values()) {
1193 channel.setImportanceLockedByCriticalDeviceFunction(true);
Julia Reynolds0c245002019-03-27 16:10:11 -04001194 }
1195 }
1196 }
1197 }
1198 }
1199
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001200 public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
1201 int uid, String groupId, boolean includeDeleted) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001202 Objects.requireNonNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001203 synchronized (mPackagePreferences) {
1204 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1205 if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
1206 return null;
1207 }
1208 NotificationChannelGroup group = r.groups.get(groupId).clone();
1209 group.setChannels(new ArrayList<>());
1210 int N = r.channels.size();
1211 for (int i = 0; i < N; i++) {
1212 final NotificationChannel nc = r.channels.valueAt(i);
1213 if (includeDeleted || !nc.isDeleted()) {
1214 if (groupId.equals(nc.getGroup())) {
1215 group.addChannel(nc);
1216 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001217 }
1218 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001219 return group;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001220 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001221 }
1222
1223 public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
1224 int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001225 Objects.requireNonNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001226 synchronized (mPackagePreferences) {
1227 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1228 if (r == null) {
1229 return null;
1230 }
1231 return r.groups.get(groupId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001232 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001233 }
1234
1235 @Override
1236 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001237 int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001238 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001239 Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001240 synchronized (mPackagePreferences) {
1241 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1242 if (r == null) {
1243 return ParceledListSlice.emptyList();
1244 }
1245 NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
1246 int N = r.channels.size();
1247 for (int i = 0; i < N; i++) {
1248 final NotificationChannel nc = r.channels.valueAt(i);
1249 if (includeDeleted || !nc.isDeleted()) {
1250 if (nc.getGroup() != null) {
1251 if (r.groups.get(nc.getGroup()) != null) {
1252 NotificationChannelGroup ncg = groups.get(nc.getGroup());
1253 if (ncg == null) {
1254 ncg = r.groups.get(nc.getGroup()).clone();
1255 ncg.setChannels(new ArrayList<>());
1256 groups.put(nc.getGroup(), ncg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001257
Julia Reynolds5c399c62019-04-08 14:42:53 -04001258 }
1259 ncg.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001260 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001261 } else {
1262 nonGrouped.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001263 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001264 }
1265 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001266 if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
1267 groups.put(null, nonGrouped);
1268 }
1269 if (includeEmpty) {
1270 for (NotificationChannelGroup group : r.groups.values()) {
1271 if (!groups.containsKey(group.getId())) {
1272 groups.put(group.getId(), group);
1273 }
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001274 }
1275 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001276 return new ParceledListSlice<>(new ArrayList<>(groups.values()));
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001277 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001278 }
1279
1280 public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
1281 String groupId) {
1282 List<NotificationChannel> deletedChannels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001283 synchronized (mPackagePreferences) {
1284 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1285 if (r == null || TextUtils.isEmpty(groupId)) {
1286 return deletedChannels;
1287 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001288
Will Brockman23db6d42020-02-28 09:51:12 -05001289 NotificationChannelGroup channelGroup = r.groups.remove(groupId);
1290 if (channelGroup != null) {
1291 mNotificationChannelLogger.logNotificationChannelGroupDeleted(channelGroup, uid,
1292 pkg);
1293 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001294
Julia Reynolds5c399c62019-04-08 14:42:53 -04001295 int N = r.channels.size();
1296 for (int i = 0; i < N; i++) {
1297 final NotificationChannel nc = r.channels.valueAt(i);
1298 if (groupId.equals(nc.getGroup())) {
Will Brockman23db6d42020-02-28 09:51:12 -05001299 deleteNotificationChannelLocked(nc, pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001300 deletedChannels.add(nc);
1301 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001302 }
1303 }
1304 return deletedChannels;
1305 }
1306
1307 @Override
1308 public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
1309 int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001310 List<NotificationChannelGroup> groups = new ArrayList<>();
1311 synchronized (mPackagePreferences) {
1312 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1313 if (r == null) {
1314 return groups;
1315 }
1316 groups.addAll(r.groups.values());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001317 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001318 return groups;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001319 }
1320
Julia Reynolds02971452020-02-14 16:44:19 -05001321 public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
1322 synchronized (mPackagePreferences) {
1323 ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
1324
1325 for (PackagePreferences p : mPackagePreferences.values()) {
1326 int N = p.channels.size();
1327 for (int i = 0; i < N; i++) {
1328 final NotificationChannel nc = p.channels.valueAt(i);
1329 if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()
Julia Reynoldsb1b9d642020-04-22 16:18:44 -04001330 && !nc.isDemoted()
Julia Reynolds02971452020-02-14 16:44:19 -05001331 && (nc.isImportantConversation() || !onlyImportant)) {
1332 ConversationChannelWrapper conversation = new ConversationChannelWrapper();
1333 conversation.setPkg(p.pkg);
1334 conversation.setUid(p.uid);
1335 conversation.setNotificationChannel(nc);
1336 conversation.setParentChannelLabel(
1337 p.channels.get(nc.getParentChannelId()).getName());
1338 boolean blockedByGroup = false;
1339 if (nc.getGroup() != null) {
1340 NotificationChannelGroup group = p.groups.get(nc.getGroup());
1341 if (group != null) {
1342 if (group.isBlocked()) {
1343 blockedByGroup = true;
1344 } else {
1345 conversation.setGroupLabel(group.getName());
1346 }
1347 }
1348 }
1349 if (!blockedByGroup) {
1350 conversations.add(conversation);
1351 }
1352 }
1353 }
1354 }
1355
1356 return conversations;
1357 }
1358 }
1359
Julia Reynolds882f2062020-02-05 12:11:38 -05001360 public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) {
1361 Objects.requireNonNull(pkg);
1362 synchronized (mPackagePreferences) {
1363 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1364 if (r == null) {
1365 return new ArrayList<>();
1366 }
1367 ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
1368 int N = r.channels.size();
1369 for (int i = 0; i < N; i++) {
1370 final NotificationChannel nc = r.channels.valueAt(i);
Julia Reynolds8582df52020-04-24 18:30:59 -04001371 if (!TextUtils.isEmpty(nc.getConversationId())
1372 && !nc.isDeleted()
1373 && !nc.isDemoted()) {
Julia Reynolds882f2062020-02-05 12:11:38 -05001374 ConversationChannelWrapper conversation = new ConversationChannelWrapper();
Julia Reynolds02971452020-02-14 16:44:19 -05001375 conversation.setPkg(r.pkg);
1376 conversation.setUid(r.uid);
Julia Reynolds882f2062020-02-05 12:11:38 -05001377 conversation.setNotificationChannel(nc);
1378 conversation.setParentChannelLabel(
1379 r.channels.get(nc.getParentChannelId()).getName());
1380 boolean blockedByGroup = false;
1381 if (nc.getGroup() != null) {
1382 NotificationChannelGroup group = r.groups.get(nc.getGroup());
1383 if (group != null) {
1384 if (group.isBlocked()) {
1385 blockedByGroup = true;
1386 } else {
1387 conversation.setGroupLabel(group.getName());
1388 }
1389 }
1390 }
1391 if (!blockedByGroup) {
1392 conversations.add(conversation);
1393 }
1394 }
1395 }
1396
1397 return conversations;
1398 }
1399 }
1400
Julia Reynoldsa625b942020-02-15 09:42:23 -05001401 public @NonNull List<String> deleteConversation(String pkg, int uid, String conversationId) {
1402 synchronized (mPackagePreferences) {
1403 List<String> deletedChannelIds = new ArrayList<>();
1404 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1405 if (r == null) {
1406 return deletedChannelIds;
1407 }
1408 int N = r.channels.size();
1409 for (int i = 0; i < N; i++) {
1410 final NotificationChannel nc = r.channels.valueAt(i);
1411 if (conversationId.equals(nc.getConversationId())) {
1412 nc.setDeleted(true);
1413 LogMaker lm = getChannelLog(nc, pkg);
1414 lm.setType(
1415 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
1416 MetricsLogger.action(lm);
Will Brockman23db6d42020-02-28 09:51:12 -05001417 mNotificationChannelLogger.logNotificationChannelDeleted(nc, uid, pkg);
Julia Reynoldsa625b942020-02-15 09:42:23 -05001418
1419 deletedChannelIds.add(nc.getId());
1420 }
1421 }
1422 if (!deletedChannelIds.isEmpty() && mAreChannelsBypassingDnd) {
1423 updateChannelsBypassingDnd(mContext.getUserId());
1424 }
1425 return deletedChannelIds;
1426 }
1427 }
1428
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001429 @Override
1430 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
1431 boolean includeDeleted) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001432 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001433 List<NotificationChannel> channels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001434 synchronized (mPackagePreferences) {
1435 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1436 if (r == null) {
1437 return ParceledListSlice.emptyList();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001438 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001439 int N = r.channels.size();
1440 for (int i = 0; i < N; i++) {
1441 final NotificationChannel nc = r.channels.valueAt(i);
1442 if (includeDeleted || !nc.isDeleted()) {
1443 channels.add(nc);
1444 }
1445 }
1446 return new ParceledListSlice<>(channels);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001447 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001448 }
1449
1450 /**
Beverly0479cde22018-11-09 11:05:34 -05001451 * Gets all notification channels associated with the given pkg and userId that can bypass dnd
1452 */
1453 public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
1454 int userId) {
1455 List<NotificationChannel> channels = new ArrayList<>();
1456 synchronized (mPackagePreferences) {
1457 final PackagePreferences r = mPackagePreferences.get(
1458 packagePreferencesKey(pkg, userId));
1459 // notifications from this package aren't blocked
1460 if (r != null && r.importance != IMPORTANCE_NONE) {
1461 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001462 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001463 channels.add(channel);
1464 }
1465 }
1466 }
1467 }
1468 return new ParceledListSlice<>(channels);
1469 }
1470
1471 /**
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001472 * True for pre-O apps that only have the default channel, or pre O apps that have no
1473 * channels yet. This method will create the default channel for pre-O apps that don't have it.
1474 * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
1475 * upgrades.
1476 */
1477 public boolean onlyHasDefaultChannel(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001478 synchronized (mPackagePreferences) {
1479 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1480 if (r.channels.size() == 1
1481 && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
1482 return true;
1483 }
1484 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001485 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001486 }
1487
1488 public int getDeletedChannelCount(String pkg, int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001489 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001490 int deletedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001491 synchronized (mPackagePreferences) {
1492 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1493 if (r == null) {
1494 return deletedCount;
1495 }
1496 int N = r.channels.size();
1497 for (int i = 0; i < N; i++) {
1498 final NotificationChannel nc = r.channels.valueAt(i);
1499 if (nc.isDeleted()) {
1500 deletedCount++;
1501 }
1502 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001503 return deletedCount;
1504 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001505 }
1506
1507 public int getBlockedChannelCount(String pkg, int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001508 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001509 int blockedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001510 synchronized (mPackagePreferences) {
1511 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1512 if (r == null) {
1513 return blockedCount;
1514 }
1515 int N = r.channels.size();
1516 for (int i = 0; i < N; i++) {
1517 final NotificationChannel nc = r.channels.valueAt(i);
1518 if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
1519 blockedCount++;
1520 }
1521 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001522 return blockedCount;
1523 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001524 }
1525
1526 public int getBlockedAppCount(int userId) {
1527 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001528 synchronized (mPackagePreferences) {
1529 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001530 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001531 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001532 if (userId == UserHandle.getUserId(r.uid)
1533 && r.importance == IMPORTANCE_NONE) {
1534 count++;
1535 }
1536 }
1537 }
1538 return count;
1539 }
1540
Beverly0479cde22018-11-09 11:05:34 -05001541 /**
1542 * Returns the number of apps that have at least one notification channel that can bypass DND
1543 * for given particular user
1544 */
1545 public int getAppsBypassingDndCount(int userId) {
1546 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001547 synchronized (mPackagePreferences) {
Beverly0479cde22018-11-09 11:05:34 -05001548 final int numPackagePreferences = mPackagePreferences.size();
1549 for (int i = 0; i < numPackagePreferences; i++) {
1550 final PackagePreferences r = mPackagePreferences.valueAt(i);
1551 // Package isn't associated with this userId or notifications from this package are
1552 // blocked
1553 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1554 continue;
1555 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001556
Beverly0479cde22018-11-09 11:05:34 -05001557 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001558 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001559 count++;
1560 break;
1561 }
1562 }
1563 }
1564 }
1565 return count;
1566 }
1567
1568 /**
1569 * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
1570 * updating
1571 * @param userId
1572 */
1573 private void syncChannelsBypassingDnd(int userId) {
1574 mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
1575 & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
1576 updateChannelsBypassingDnd(userId);
1577 }
1578
1579 /**
1580 * Updates the user's NotificationPolicy based on whether the given userId
1581 * has channels bypassing DND
1582 * @param userId
1583 */
1584 private void updateChannelsBypassingDnd(int userId) {
1585 synchronized (mPackagePreferences) {
1586 final int numPackagePreferences = mPackagePreferences.size();
1587 for (int i = 0; i < numPackagePreferences; i++) {
1588 final PackagePreferences r = mPackagePreferences.valueAt(i);
1589 // Package isn't associated with this userId or notifications from this package are
1590 // blocked
1591 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1592 continue;
1593 }
1594
1595 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001596 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001597 if (!mAreChannelsBypassingDnd) {
1598 mAreChannelsBypassingDnd = true;
1599 updateZenPolicy(true);
1600 }
1601 return;
1602 }
1603 }
1604 }
1605 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001606 // If no channels bypass DND, update the zen policy once to disable DND bypass.
1607 if (mAreChannelsBypassingDnd) {
1608 mAreChannelsBypassingDnd = false;
1609 updateZenPolicy(false);
1610 }
1611 }
1612
Julia Reynolds5c399c62019-04-08 14:42:53 -04001613 private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
Beverly0479cde22018-11-09 11:05:34 -05001614 // Channel is in a group that's blocked
Beverly4f7b53d2018-11-20 09:56:31 -05001615 if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
1616 return false;
Beverly0479cde22018-11-09 11:05:34 -05001617 }
1618
1619 // Channel is deleted or is blocked
1620 if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
1621 return false;
1622 }
1623
1624 return true;
1625 }
1626
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001627 public void updateZenPolicy(boolean areChannelsBypassingDnd) {
1628 NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
1629 mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
1630 policy.priorityCategories, policy.priorityCallSenders,
1631 policy.priorityMessageSenders, policy.suppressedVisualEffects,
1632 (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
Julia Reynolds24edc002020-01-29 16:35:32 -05001633 : 0),
1634 policy.priorityConversationSenders));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001635 }
1636
1637 public boolean areChannelsBypassingDnd() {
1638 return mAreChannelsBypassingDnd;
1639 }
1640
1641 /**
1642 * Sets importance.
1643 */
1644 @Override
1645 public void setImportance(String pkgName, int uid, int importance) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001646 synchronized (mPackagePreferences) {
1647 getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance;
1648 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001649 updateConfig();
1650 }
1651
1652 public void setEnabled(String packageName, int uid, boolean enabled) {
1653 boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
1654 if (wasEnabled == enabled) {
1655 return;
1656 }
1657 setImportance(packageName, uid,
1658 enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
Will Brockman7575e052020-06-16 15:05:12 -04001659 mNotificationChannelLogger.logAppNotificationsAllowed(uid, packageName, enabled);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001660 }
1661
1662 /**
1663 * Sets whether any notifications from the app, represented by the given {@code pkgName} and
1664 * {@code uid}, have their importance locked by the user. Locked notifications don't get
1665 * considered for sentiment adjustments (and thus never show a blocking helper).
1666 */
1667 public void setAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001668 synchronized (mPackagePreferences) {
1669 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(packageName, uid);
1670 if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
1671 return;
1672 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001673
Julia Reynolds5c399c62019-04-08 14:42:53 -04001674 prefs.lockedAppFields =
1675 prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
1676 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001677 updateConfig();
1678 }
1679
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001680 /**
1681 * Returns the delegate for a given package, if it's allowed by the package and the user.
1682 */
1683 public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001684 synchronized (mPackagePreferences) {
1685 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001686
Julia Reynolds5c399c62019-04-08 14:42:53 -04001687 if (prefs == null || prefs.delegate == null) {
1688 return null;
1689 }
1690 if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
1691 return null;
1692 }
1693 return prefs.delegate.mPkg;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001694 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001695 }
1696
1697 /**
1698 * Used by an app to delegate notification posting privileges to another apps.
1699 */
1700 public void setNotificationDelegate(String sourcePkg, int sourceUid,
1701 String delegatePkg, int delegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001702 synchronized (mPackagePreferences) {
1703 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001704
Julia Reynolds5c399c62019-04-08 14:42:53 -04001705 boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
1706 Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
1707 prefs.delegate = delegate;
1708 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001709 updateConfig();
1710 }
1711
1712 /**
1713 * Used by an app to turn off its notification delegate.
1714 */
1715 public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001716 boolean changed = false;
1717 synchronized (mPackagePreferences) {
1718 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1719 if (prefs != null && prefs.delegate != null) {
1720 prefs.delegate.mEnabled = false;
1721 changed = true;
1722 }
1723 }
1724 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001725 updateConfig();
1726 }
1727 }
1728
1729 /**
1730 * Toggles whether an app can have a notification delegate on behalf of a user.
1731 */
1732 public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001733 boolean changed = false;
1734 synchronized (mPackagePreferences) {
1735 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1736 if (prefs != null && prefs.delegate != null) {
1737 prefs.delegate.mUserAllowed = userAllowed;
1738 changed = true;
1739 }
1740 }
1741 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001742 updateConfig();
1743 }
1744 }
1745
1746 /**
1747 * Returns whether the given app is allowed on post notifications on behalf of the other given
1748 * app.
1749 */
1750 public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
1751 String potentialDelegatePkg, int potentialDelegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001752 synchronized (mPackagePreferences) {
1753 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001754
Julia Reynolds5c399c62019-04-08 14:42:53 -04001755 return prefs != null && prefs.isValidDelegate(potentialDelegatePkg,
1756 potentialDelegateUid);
1757 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001758 }
1759
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001760 @VisibleForTesting
Julia Reynolds5c399c62019-04-08 14:42:53 -04001761 void lockFieldsForUpdateLocked(NotificationChannel original, NotificationChannel update) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001762 if (original.canBypassDnd() != update.canBypassDnd()) {
1763 update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
1764 }
1765 if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
1766 update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
1767 }
1768 if (original.getImportance() != update.getImportance()) {
1769 update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
1770 }
1771 if (original.shouldShowLights() != update.shouldShowLights()
1772 || original.getLightColor() != update.getLightColor()) {
1773 update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
1774 }
1775 if (!Objects.equals(original.getSound(), update.getSound())) {
1776 update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
1777 }
1778 if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
1779 || original.shouldVibrate() != update.shouldVibrate()) {
1780 update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
1781 }
1782 if (original.canShowBadge() != update.canShowBadge()) {
1783 update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
1784 }
Mady Melloree8d5b59e2020-04-09 14:19:53 -07001785 if (original.getAllowBubbles() != update.getAllowBubbles()) {
Mady Mellorc39b4ae2019-01-09 17:11:37 -08001786 update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE);
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -04001787 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001788 }
1789
1790 public void dump(PrintWriter pw, String prefix,
1791 @NonNull NotificationManagerService.DumpFilter filter) {
1792 pw.print(prefix);
1793 pw.println("per-package config:");
1794
Julia Reynolds5c399c62019-04-08 14:42:53 -04001795 pw.println("PackagePreferences:");
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001796 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001797 dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001798 }
1799 pw.println("Restored without uid:");
Julia Reynolds5c399c62019-04-08 14:42:53 -04001800 dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001801 }
1802
1803 public void dump(ProtoOutputStream proto,
1804 @NonNull NotificationManagerService.DumpFilter filter) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001805 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001806 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001807 mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001808 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001809 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001810 mRestoredWithoutUids);
1811 }
1812
Julia Reynolds5c399c62019-04-08 14:42:53 -04001813 private static void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001814 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001815 ArrayMap<String, PackagePreferences> packagePreferences) {
1816 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001817 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001818 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001819 if (filter.matches(r.pkg)) {
1820 pw.print(prefix);
1821 pw.print(" AppSettings: ");
1822 pw.print(r.pkg);
1823 pw.print(" (");
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001824 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001825 pw.print(')');
1826 if (r.importance != DEFAULT_IMPORTANCE) {
1827 pw.print(" importance=");
1828 pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
1829 }
1830 if (r.priority != DEFAULT_PRIORITY) {
1831 pw.print(" priority=");
1832 pw.print(Notification.priorityToString(r.priority));
1833 }
1834 if (r.visibility != DEFAULT_VISIBILITY) {
1835 pw.print(" visibility=");
1836 pw.print(Notification.visibilityToString(r.visibility));
1837 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001838 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1839 pw.print(" showBadge=");
1840 pw.print(r.showBadge);
1841 }
1842 if (r.defaultAppLockedImportance != DEFAULT_APP_LOCKED_IMPORTANCE) {
1843 pw.print(" defaultAppLocked=");
1844 pw.print(r.defaultAppLockedImportance);
1845 }
1846 if (r.oemLockedImportance != DEFAULT_OEM_LOCKED_IMPORTANCE) {
1847 pw.print(" oemLocked=");
1848 pw.print(r.oemLockedImportance);
1849 }
Julia Reynolds72b28442019-11-12 11:43:39 -05001850 if (!r.oemLockedChannels.isEmpty()) {
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001851 pw.print(" futureLockedChannels=");
Julia Reynolds72b28442019-11-12 11:43:39 -05001852 pw.print(r.oemLockedChannels);
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001853 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001854 pw.println();
1855 for (NotificationChannel channel : r.channels.values()) {
1856 pw.print(prefix);
1857 channel.dump(pw, " ", filter.redact);
1858 }
1859 for (NotificationChannelGroup group : r.groups.values()) {
1860 pw.print(prefix);
1861 pw.print(" ");
1862 pw.print(" ");
1863 pw.println(group);
1864 }
1865 }
1866 }
1867 }
1868
Julia Reynolds5c399c62019-04-08 14:42:53 -04001869 private static void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001870 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001871 ArrayMap<String, PackagePreferences> packagePreferences) {
1872 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001873 long fToken;
1874 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001875 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001876 if (filter.matches(r.pkg)) {
1877 fToken = proto.start(fieldId);
1878
1879 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
1880 proto.write(RankingHelperProto.RecordProto.UID, r.uid);
1881 proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
1882 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
1883 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
1884 proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
1885
1886 for (NotificationChannel channel : r.channels.values()) {
Jeffrey Huangcb782852019-12-05 11:28:11 -08001887 channel.dumpDebug(proto, RankingHelperProto.RecordProto.CHANNELS);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001888 }
1889 for (NotificationChannelGroup group : r.groups.values()) {
Jeffrey Huangcb782852019-12-05 11:28:11 -08001890 group.dumpDebug(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001891 }
1892
1893 proto.end(fToken);
1894 }
1895 }
1896 }
1897
Yotam Aron74299972020-01-16 16:20:58 +02001898 /**
1899 * Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}.
1900 */
1901 public void pullPackagePreferencesStats(List<StatsEvent> events) {
1902 synchronized (mPackagePreferences) {
1903 for (int i = 0; i < mPackagePreferences.size(); i++) {
1904 if (i > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
1905 break;
1906 }
Chris Wren1a934a32020-05-19 13:45:46 -04001907 SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
Yotam Aron74299972020-01-16 16:20:58 +02001908 .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
1909 final PackagePreferences r = mPackagePreferences.valueAt(i);
1910 event.writeInt(r.uid);
Muhammad Qureshi22e52da2020-03-30 21:14:21 -07001911 event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
Yotam Aron74299972020-01-16 16:20:58 +02001912 event.writeInt(r.importance);
1913 event.writeInt(r.visibility);
1914 event.writeInt(r.lockedAppFields);
1915 events.add(event.build());
1916 }
1917 }
1918 }
1919
1920 /**
1921 * Fills out {@link PackageNotificationChannelPreferences} proto and wraps it in a
1922 * {@link StatsEvent}.
1923 */
1924 public void pullPackageChannelPreferencesStats(List<StatsEvent> events) {
1925 synchronized (mPackagePreferences) {
1926 int totalChannelsPulled = 0;
1927 for (int i = 0; i < mPackagePreferences.size(); i++) {
1928 if (totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
1929 break;
1930 }
1931 final PackagePreferences r = mPackagePreferences.valueAt(i);
1932 for (NotificationChannel channel : r.channels.values()) {
1933 if (++totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
1934 break;
1935 }
Chris Wren1a934a32020-05-19 13:45:46 -04001936 SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
Yotam Aron74299972020-01-16 16:20:58 +02001937 .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
1938 event.writeInt(r.uid);
Muhammad Qureshi22e52da2020-03-30 21:14:21 -07001939 event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
Yotam Aron74299972020-01-16 16:20:58 +02001940 event.writeString(channel.getId());
1941 event.writeString(channel.getName().toString());
1942 event.writeString(channel.getDescription());
1943 event.writeInt(channel.getImportance());
1944 event.writeInt(channel.getUserLockedFields());
1945 event.writeBoolean(channel.isDeleted());
Chris Wren07cb6a42020-05-14 17:35:30 -04001946 event.writeBoolean(channel.getConversationId() != null);
1947 event.writeBoolean(channel.isDemoted());
1948 event.writeBoolean(channel.isImportantConversation());
Yotam Aron74299972020-01-16 16:20:58 +02001949 events.add(event.build());
1950 }
1951 }
1952 }
1953 }
1954
1955 /**
1956 * Fills out {@link PackageNotificationChannelGroupPreferences} proto and wraps it in a
1957 * {@link StatsEvent}.
1958 */
1959 public void pullPackageChannelGroupPreferencesStats(List<StatsEvent> events) {
1960 synchronized (mPackagePreferences) {
1961 int totalGroupsPulled = 0;
1962 for (int i = 0; i < mPackagePreferences.size(); i++) {
1963 if (totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
1964 break;
1965 }
1966 final PackagePreferences r = mPackagePreferences.valueAt(i);
1967 for (NotificationChannelGroup groupChannel : r.groups.values()) {
1968 if (++totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
1969 break;
1970 }
Chris Wren1a934a32020-05-19 13:45:46 -04001971 SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
Yotam Aron74299972020-01-16 16:20:58 +02001972 .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
1973 event.writeInt(r.uid);
Muhammad Qureshi22e52da2020-03-30 21:14:21 -07001974 event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
Yotam Aron74299972020-01-16 16:20:58 +02001975 event.writeString(groupChannel.getId());
1976 event.writeString(groupChannel.getName().toString());
1977 event.writeString(groupChannel.getDescription());
1978 event.writeBoolean(groupChannel.isBlocked());
1979 event.writeInt(groupChannel.getUserLockedFields());
1980 events.add(event.build());
1981 }
1982 }
1983 }
1984 }
1985
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001986 public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
1987 JSONObject ranking = new JSONObject();
1988 JSONArray PackagePreferencess = new JSONArray();
1989 try {
1990 ranking.put("noUid", mRestoredWithoutUids.size());
1991 } catch (JSONException e) {
1992 // pass
1993 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001994 synchronized (mPackagePreferences) {
1995 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001996 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001997 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001998 if (filter == null || filter.matches(r.pkg)) {
1999 JSONObject PackagePreferences = new JSONObject();
2000 try {
2001 PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
2002 PackagePreferences.put("packageName", r.pkg);
2003 if (r.importance != DEFAULT_IMPORTANCE) {
2004 PackagePreferences.put("importance",
2005 NotificationListenerService.Ranking.importanceToString(
2006 r.importance));
2007 }
2008 if (r.priority != DEFAULT_PRIORITY) {
2009 PackagePreferences.put("priority",
2010 Notification.priorityToString(r.priority));
2011 }
2012 if (r.visibility != DEFAULT_VISIBILITY) {
2013 PackagePreferences.put("visibility",
2014 Notification.visibilityToString(r.visibility));
2015 }
2016 if (r.showBadge != DEFAULT_SHOW_BADGE) {
2017 PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
2018 }
2019 JSONArray channels = new JSONArray();
2020 for (NotificationChannel channel : r.channels.values()) {
2021 channels.put(channel.toJson());
2022 }
2023 PackagePreferences.put("channels", channels);
2024 JSONArray groups = new JSONArray();
2025 for (NotificationChannelGroup group : r.groups.values()) {
2026 groups.put(group.toJson());
2027 }
2028 PackagePreferences.put("groups", groups);
2029 } catch (JSONException e) {
2030 // pass
2031 }
2032 PackagePreferencess.put(PackagePreferences);
2033 }
2034 }
2035 }
2036 try {
2037 ranking.put("PackagePreferencess", PackagePreferencess);
2038 } catch (JSONException e) {
2039 // pass
2040 }
2041 return ranking;
2042 }
2043
2044 /**
2045 * Dump only the ban information as structured JSON for the stats collector.
2046 *
2047 * This is intentionally redundant with {#link dumpJson} because the old
2048 * scraper will expect this format.
2049 *
2050 * @param filter
2051 * @return
2052 */
2053 public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
2054 JSONArray bans = new JSONArray();
2055 Map<Integer, String> packageBans = getPackageBans();
2056 for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
2057 final int userId = UserHandle.getUserId(ban.getKey());
2058 final String packageName = ban.getValue();
2059 if (filter == null || filter.matches(packageName)) {
2060 JSONObject banJson = new JSONObject();
2061 try {
2062 banJson.put("userId", userId);
2063 banJson.put("packageName", packageName);
2064 } catch (JSONException e) {
2065 e.printStackTrace();
2066 }
2067 bans.put(banJson);
2068 }
2069 }
2070 return bans;
2071 }
2072
2073 public Map<Integer, String> getPackageBans() {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002074 synchronized (mPackagePreferences) {
2075 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002076 ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
2077 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002078 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002079 if (r.importance == IMPORTANCE_NONE) {
2080 packageBans.put(r.uid, r.pkg);
2081 }
2082 }
2083
2084 return packageBans;
2085 }
2086 }
2087
2088 /**
2089 * Dump only the channel information as structured JSON for the stats collector.
2090 *
2091 * This is intentionally redundant with {#link dumpJson} because the old
2092 * scraper will expect this format.
2093 *
2094 * @param filter
2095 * @return
2096 */
2097 public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
2098 JSONArray channels = new JSONArray();
2099 Map<String, Integer> packageChannels = getPackageChannels();
2100 for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) {
2101 final String packageName = channelCount.getKey();
2102 if (filter == null || filter.matches(packageName)) {
2103 JSONObject channelCountJson = new JSONObject();
2104 try {
2105 channelCountJson.put("packageName", packageName);
2106 channelCountJson.put("channelCount", channelCount.getValue());
2107 } catch (JSONException e) {
2108 e.printStackTrace();
2109 }
2110 channels.put(channelCountJson);
2111 }
2112 }
2113 return channels;
2114 }
2115
2116 private Map<String, Integer> getPackageChannels() {
2117 ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002118 synchronized (mPackagePreferences) {
2119 for (int i = 0; i < mPackagePreferences.size(); i++) {
2120 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002121 int channelCount = 0;
2122 for (int j = 0; j < r.channels.size(); j++) {
2123 if (!r.channels.valueAt(j).isDeleted()) {
2124 channelCount++;
2125 }
2126 }
2127 packageChannels.put(r.pkg, channelCount);
2128 }
2129 }
2130 return packageChannels;
2131 }
2132
Beverly0479cde22018-11-09 11:05:34 -05002133 /**
2134 * Called when user switches
2135 */
2136 public void onUserSwitched(int userId) {
2137 syncChannelsBypassingDnd(userId);
2138 }
2139
2140 /**
2141 * Called when user is unlocked
2142 */
2143 public void onUserUnlocked(int userId) {
2144 syncChannelsBypassingDnd(userId);
2145 }
2146
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002147 public void onUserRemoved(int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002148 synchronized (mPackagePreferences) {
2149 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002150 for (int i = N - 1; i >= 0; i--) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002151 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002152 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002153 mPackagePreferences.removeAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002154 }
2155 }
2156 }
2157 }
2158
2159 protected void onLocaleChanged(Context context, int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002160 synchronized (mPackagePreferences) {
2161 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002162 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002163 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002164 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
2165 if (PackagePreferences.channels.containsKey(
2166 NotificationChannel.DEFAULT_CHANNEL_ID)) {
2167 PackagePreferences.channels.get(
2168 NotificationChannel.DEFAULT_CHANNEL_ID).setName(
2169 context.getResources().getString(
2170 R.string.default_notification_channel_label));
2171 }
2172 }
2173 }
2174 }
2175 }
2176
Julia Reynolds996c7c12019-05-24 10:25:33 -04002177 public boolean onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002178 int[] uidList) {
2179 if (pkgList == null || pkgList.length == 0) {
Julia Reynolds996c7c12019-05-24 10:25:33 -04002180 return false; // nothing to do
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002181 }
2182 boolean updated = false;
2183 if (removingPackage) {
2184 // Remove notification settings for uninstalled package
2185 int size = Math.min(pkgList.length, uidList.length);
2186 for (int i = 0; i < size; i++) {
2187 final String pkg = pkgList[i];
2188 final int uid = uidList[i];
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002189 synchronized (mPackagePreferences) {
2190 mPackagePreferences.remove(packagePreferencesKey(pkg, uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002191 }
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002192 mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002193 updated = true;
2194 }
2195 } else {
2196 for (String pkg : pkgList) {
2197 // Package install
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002198 final PackagePreferences r =
2199 mRestoredWithoutUids.get(unrestoredPackageKey(pkg, changeUserId));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002200 if (r != null) {
2201 try {
2202 r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002203 mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId));
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002204 synchronized (mPackagePreferences) {
2205 mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002206 }
2207 updated = true;
2208 } catch (PackageManager.NameNotFoundException e) {
2209 // noop
2210 }
2211 }
2212 // Package upgrade
2213 try {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002214 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04002215 PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002216 mPm.getPackageUidAsUser(pkg, changeUserId));
2217 if (fullPackagePreferences != null) {
Julia Reynolds996c7c12019-05-24 10:25:33 -04002218 updated |= createDefaultChannelIfNeededLocked(fullPackagePreferences);
2219 updated |= deleteDefaultChannelIfNeededLocked(fullPackagePreferences);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002220 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002221 }
2222 } catch (PackageManager.NameNotFoundException e) {
2223 }
2224 }
2225 }
2226
2227 if (updated) {
2228 updateConfig();
2229 }
Julia Reynolds996c7c12019-05-24 10:25:33 -04002230 return updated;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002231 }
2232
Julia Reynolds7af51c52019-04-19 11:08:27 -04002233 public void clearData(String pkg, int uid) {
2234 synchronized (mPackagePreferences) {
2235 PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
2236 if (p != null) {
2237 p.channels = new ArrayMap<>();
2238 p.groups = new ArrayMap<>();
2239 p.delegate = null;
2240 p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Mady Mellora92268c2020-03-09 17:25:08 -07002241 p.bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
Julia Reynolds7af51c52019-04-19 11:08:27 -04002242 p.importance = DEFAULT_IMPORTANCE;
2243 p.priority = DEFAULT_PRIORITY;
2244 p.visibility = DEFAULT_VISIBILITY;
2245 p.showBadge = DEFAULT_SHOW_BADGE;
2246 }
2247 }
2248 }
2249
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002250 private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
2251 return new LogMaker(
2252 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2253 .ACTION_NOTIFICATION_CHANNEL)
2254 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
2255 .setPackageName(pkg)
2256 .addTaggedData(
2257 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2258 .FIELD_NOTIFICATION_CHANNEL_ID,
2259 channel.getId())
2260 .addTaggedData(
2261 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2262 .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
2263 channel.getImportance());
2264 }
2265
2266 private LogMaker getChannelGroupLog(String groupId, String pkg) {
2267 return new LogMaker(
2268 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2269 .ACTION_NOTIFICATION_CHANNEL_GROUP)
2270 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
2271 .addTaggedData(
2272 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2273 .FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
2274 groupId)
2275 .setPackageName(pkg);
2276 }
2277
Julia Reynolds4509ce72019-01-31 13:12:43 -05002278 public void updateBubblesEnabled() {
Lyn Han4463f842019-07-09 15:27:28 -07002279 final boolean newValue = Settings.Global.getInt(mContext.getContentResolver(),
2280 Settings.Global.NOTIFICATION_BUBBLES,
Mady Mellora92268c2020-03-09 17:25:08 -07002281 DEFAULT_GLOBAL_ALLOW_BUBBLE ? 1 : 0) == 1;
2282 if (newValue != mBubblesEnabledGlobally) {
2283 mBubblesEnabledGlobally = newValue;
Julia Reynolds4509ce72019-01-31 13:12:43 -05002284 updateConfig();
2285 }
2286 }
2287
Lyn Han4463f842019-07-09 15:27:28 -07002288 public boolean bubblesEnabled() {
Mady Mellora92268c2020-03-09 17:25:08 -07002289 return mBubblesEnabledGlobally;
Julia Reynolds4509ce72019-01-31 13:12:43 -05002290 }
2291
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002292 public void updateBadgingEnabled() {
2293 if (mBadgingEnabled == null) {
2294 mBadgingEnabled = new SparseBooleanArray();
2295 }
2296 boolean changed = false;
2297 // update the cached values
2298 for (int index = 0; index < mBadgingEnabled.size(); index++) {
2299 int userId = mBadgingEnabled.keyAt(index);
2300 final boolean oldValue = mBadgingEnabled.get(userId);
2301 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2302 Settings.Secure.NOTIFICATION_BADGING,
2303 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
2304 mBadgingEnabled.put(userId, newValue);
2305 changed |= oldValue != newValue;
2306 }
2307 if (changed) {
2308 updateConfig();
2309 }
2310 }
2311
2312 public boolean badgingEnabled(UserHandle userHandle) {
2313 int userId = userHandle.getIdentifier();
2314 if (userId == UserHandle.USER_ALL) {
2315 return false;
2316 }
2317 if (mBadgingEnabled.indexOfKey(userId) < 0) {
2318 mBadgingEnabled.put(userId,
2319 Settings.Secure.getIntForUser(mContext.getContentResolver(),
2320 Settings.Secure.NOTIFICATION_BADGING,
2321 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
2322 }
2323 return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
2324 }
2325
2326 private void updateConfig() {
2327 mRankingHandler.requestSort();
2328 }
2329
2330 private static String packagePreferencesKey(String pkg, int uid) {
2331 return pkg + "|" + uid;
2332 }
2333
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002334 private static String unrestoredPackageKey(String pkg, @UserIdInt int userId) {
2335 return pkg + "|" + userId;
2336 }
2337
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002338 private static class PackagePreferences {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002339 String pkg;
2340 int uid = UNKNOWN_UID;
2341 int importance = DEFAULT_IMPORTANCE;
2342 int priority = DEFAULT_PRIORITY;
2343 int visibility = DEFAULT_VISIBILITY;
2344 boolean showBadge = DEFAULT_SHOW_BADGE;
Mady Mellora92268c2020-03-09 17:25:08 -07002345 int bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002346 int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Julia Reynolds0c245002019-03-27 16:10:11 -04002347 // these fields are loaded on boot from a different source of truth and so are not
2348 // written to notification policy xml
Julia Reynolds413ba842019-01-11 10:38:08 -05002349 boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
Julia Reynolds72b28442019-11-12 11:43:39 -05002350 List<String> oemLockedChannels = new ArrayList<>();
Julia Reynolds0c245002019-03-27 16:10:11 -04002351 boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE;
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -04002352
2353 boolean hasSentInvalidMessage = false;
2354 boolean hasSentValidMessage = false;
2355 // notE: only valid while hasSentMessage is false and hasSentInvalidMessage is true
2356 boolean userDemotedMsgApp = false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002357
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04002358 Delegate delegate = null;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002359 ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
2360 Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04002361
2362 public boolean isValidDelegate(String pkg, int uid) {
2363 return delegate != null && delegate.isAllowed(pkg, uid);
2364 }
2365 }
2366
2367 private static class Delegate {
2368 static final boolean DEFAULT_ENABLED = true;
2369 static final boolean DEFAULT_USER_ALLOWED = true;
2370 String mPkg;
2371 int mUid = UNKNOWN_UID;
2372 boolean mEnabled = DEFAULT_ENABLED;
2373 boolean mUserAllowed = DEFAULT_USER_ALLOWED;
2374
2375 Delegate(String pkg, int uid, boolean enabled, boolean userAllowed) {
2376 mPkg = pkg;
2377 mUid = uid;
2378 mEnabled = enabled;
2379 mUserAllowed = userAllowed;
2380 }
2381
2382 public boolean isAllowed(String pkg, int uid) {
2383 if (pkg == null || uid == UNKNOWN_UID) {
2384 return false;
2385 }
2386 return pkg.equals(mPkg)
2387 && uid == mUid
2388 && (mUserAllowed && mEnabled);
2389 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002390 }
2391}