blob: dd6a83bc3f60594bb4a9fed21b62b9fc7fca8bf9 [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;
Robert Snoebergerdaf50b52020-06-18 21:40:30 -0400137 static final boolean DEFAULT_MEDIA_NOTIFICATION_FILTERING = true;
Mady Mellora92268c2020-03-09 17:25:08 -0700138
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400139 /**
140 * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
141 * fields.
142 */
143 private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
Chris Wren1a934a32020-05-19 13:45:46 -0400144 private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400145
146 /**
147 * All user-lockable fields for a given application.
148 */
149 @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
150 public @interface LockableAppFields {
151 int USER_LOCKED_IMPORTANCE = 0x00000001;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800152 int USER_LOCKED_BUBBLE = 0x00000002;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400153 }
154
155 // pkg|uid => PackagePreferences
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400156 private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>();
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500157 // pkg|userId => PackagePreferences
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400158 private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
159
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400160 private final Context mContext;
161 private final PackageManager mPm;
162 private final RankingHandler mRankingHandler;
163 private final ZenModeHelper mZenModeHelper;
Will Brockman23db6d42020-02-28 09:51:12 -0500164 private final NotificationChannelLogger mNotificationChannelLogger;
Mady Mellorc888d512020-04-09 14:48:02 -0700165 private final AppOpsManager mAppOps;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400166
167 private SparseBooleanArray mBadgingEnabled;
Mady Mellora92268c2020-03-09 17:25:08 -0700168 private boolean mBubblesEnabledGlobally = DEFAULT_GLOBAL_ALLOW_BUBBLE;
Robert Snoebergerdaf50b52020-06-18 21:40:30 -0400169 private boolean mIsMediaNotificationFilteringEnabled = DEFAULT_MEDIA_NOTIFICATION_FILTERING;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400170 private boolean mAreChannelsBypassingDnd;
Julia Reynolds2594b472019-04-03 13:30:16 -0400171 private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400172
Julia Reynolds7c267522020-01-16 11:26:41 -0500173 private boolean mAllowInvalidShortcuts = false;
174
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400175 public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
Mady Mellorc888d512020-04-09 14:48:02 -0700176 ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger,
Chris Wren1a934a32020-05-19 13:45:46 -0400177 AppOpsManager appOpsManager,
178 SysUiStatsEvent.BuilderFactory statsEventBuilderFactory) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400179 mContext = context;
180 mZenModeHelper = zenHelper;
181 mRankingHandler = rankingHandler;
182 mPm = pm;
Will Brockman23db6d42020-02-28 09:51:12 -0500183 mNotificationChannelLogger = notificationChannelLogger;
Mady Mellorc888d512020-04-09 14:48:02 -0700184 mAppOps = appOpsManager;
Chris Wren1a934a32020-05-19 13:45:46 -0400185 mStatsEventBuilderFactory = statsEventBuilderFactory;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400186
187 updateBadgingEnabled();
Julia Reynolds4509ce72019-01-31 13:12:43 -0500188 updateBubblesEnabled();
Robert Snoebergerdaf50b52020-06-18 21:40:30 -0400189 updateMediaNotificationFilteringEnabled();
Beverly0479cde22018-11-09 11:05:34 -0500190 syncChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400191 }
192
Annie Meng8b646fd2019-02-01 18:46:42 +0000193 public void readXml(XmlPullParser parser, boolean forRestore, int userId)
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400194 throws XmlPullParserException, IOException {
195 int type = parser.getEventType();
196 if (type != XmlPullParser.START_TAG) return;
197 String tag = parser.getName();
198 if (!TAG_RANKING.equals(tag)) return;
Mady Mellorc888d512020-04-09 14:48:02 -0700199
200 boolean upgradeForBubbles = false;
201 if (parser.getAttributeCount() > 0) {
202 String attribute = parser.getAttributeName(0);
203 if (ATT_VERSION.equals(attribute)) {
204 int xmlVersion = Integer.parseInt(parser.getAttributeValue(0));
205 upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
206 }
207 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400208 synchronized (mPackagePreferences) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400209 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
210 tag = parser.getName();
211 if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
212 return;
213 }
214 if (type == XmlPullParser.START_TAG) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500215 if (TAG_STATUS_ICONS.equals(tag)) {
Annie Meng8b646fd2019-02-01 18:46:42 +0000216 if (forRestore && userId != UserHandle.USER_SYSTEM) {
217 continue;
218 }
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500219 mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute(
220 parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
221 } else if (TAG_PACKAGE.equals(tag)) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400222 int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
223 String name = parser.getAttributeValue(null, ATT_NAME);
224 if (!TextUtils.isEmpty(name)) {
225 if (forRestore) {
226 try {
Annie Meng8b646fd2019-02-01 18:46:42 +0000227 uid = mPm.getPackageUidAsUser(name, userId);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400228 } catch (PackageManager.NameNotFoundException e) {
229 // noop
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400230 }
231 }
Julia Reynoldsc29370a2019-08-20 16:08:42 -0400232 boolean skipWarningLogged = false;
Mady Mellorc888d512020-04-09 14:48:02 -0700233 boolean hasSAWPermission = false;
Mady Mellorbccdf452020-05-27 11:57:12 -0700234 if (upgradeForBubbles && uid != UNKNOWN_UID) {
Mady Mellorc888d512020-04-09 14:48:02 -0700235 hasSAWPermission = mAppOps.noteOpNoThrow(
236 OP_SYSTEM_ALERT_WINDOW, uid, name, null,
237 "check-notif-bubble") == AppOpsManager.MODE_ALLOWED;
238 }
239 int bubblePref = hasSAWPermission
240 ? BUBBLE_PREFERENCE_ALL
241 : XmlUtils.readIntAttribute(parser, ATT_ALLOW_BUBBLE,
242 DEFAULT_BUBBLE_PREFERENCE);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400243
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500244 PackagePreferences r = getOrCreatePackagePreferencesLocked(
245 name, userId, uid,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400246 XmlUtils.readIntAttribute(
247 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
248 XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
249 DEFAULT_PRIORITY),
250 XmlUtils.readIntAttribute(
251 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
252 XmlUtils.readBooleanAttribute(
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500253 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
Mady Mellorc888d512020-04-09 14:48:02 -0700254 bubblePref);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400255 r.importance = XmlUtils.readIntAttribute(
256 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
257 r.priority = XmlUtils.readIntAttribute(
258 parser, ATT_PRIORITY, DEFAULT_PRIORITY);
259 r.visibility = XmlUtils.readIntAttribute(
260 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
261 r.showBadge = XmlUtils.readBooleanAttribute(
262 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
263 r.lockedAppFields = XmlUtils.readIntAttribute(parser,
264 ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400265 r.hasSentInvalidMessage = XmlUtils.readBooleanAttribute(
266 parser, ATT_SENT_INVALID_MESSAGE, false);
267 r.hasSentValidMessage = XmlUtils.readBooleanAttribute(
268 parser, ATT_SENT_VALID_MESSAGE, false);
269 r.userDemotedMsgApp = XmlUtils.readBooleanAttribute(
270 parser, ATT_USER_DEMOTED_INVALID_MSG_APP, false);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400271
272 final int innerDepth = parser.getDepth();
273 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
274 && (type != XmlPullParser.END_TAG
275 || parser.getDepth() > innerDepth)) {
276 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
277 continue;
278 }
279
280 String tagName = parser.getName();
281 // Channel groups
282 if (TAG_GROUP.equals(tagName)) {
283 String id = parser.getAttributeValue(null, ATT_ID);
284 CharSequence groupName = parser.getAttributeValue(null,
285 ATT_NAME);
286 if (!TextUtils.isEmpty(id)) {
287 NotificationChannelGroup group
288 = new NotificationChannelGroup(id, groupName);
289 group.populateFromXml(parser);
290 r.groups.put(id, group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400291 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400292 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400293 // Channels
294 if (TAG_CHANNEL.equals(tagName)) {
Julia Reynoldsc29370a2019-08-20 16:08:42 -0400295 if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
296 if (!skipWarningLogged) {
297 Slog.w(TAG, "Skipping further channels for " + r.pkg
298 + "; app has too many");
299 skipWarningLogged = true;
300 }
301 continue;
302 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400303 String id = parser.getAttributeValue(null, ATT_ID);
304 String channelName = parser.getAttributeValue(null, ATT_NAME);
305 int channelImportance = XmlUtils.readIntAttribute(
306 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
307 if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
308 NotificationChannel channel = new NotificationChannel(id,
309 channelName, channelImportance);
310 if (forRestore) {
311 channel.populateFromXmlForRestore(parser, mContext);
312 } else {
313 channel.populateFromXml(parser);
314 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -0400315 channel.setImportanceLockedByCriticalDeviceFunction(
316 r.defaultAppLockedImportance);
Julia Reynolds7c267522020-01-16 11:26:41 -0500317 boolean isInvalidShortcutChannel =
318 channel.getConversationId() != null &&
319 channel.getConversationId().contains(
320 PLACEHOLDER_CONVERSATION_ID);
321 if (mAllowInvalidShortcuts || (!mAllowInvalidShortcuts
322 && !isInvalidShortcutChannel)) {
323 r.channels.put(id, channel);
324 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400325 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400326 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400327 // Delegate
328 if (TAG_DELEGATE.equals(tagName)) {
329 int delegateId =
330 XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
331 String delegateName =
332 XmlUtils.readStringAttribute(parser, ATT_NAME);
333 boolean delegateEnabled = XmlUtils.readBooleanAttribute(
334 parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
335 boolean userAllowed = XmlUtils.readBooleanAttribute(
336 parser, ATT_USER_ALLOWED,
337 Delegate.DEFAULT_USER_ALLOWED);
338 Delegate d = null;
339 if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(
340 delegateName)) {
341 d = new Delegate(
342 delegateName, delegateId, delegateEnabled,
343 userAllowed);
344 }
345 r.delegate = d;
346 }
347
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400348 }
349
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400350 try {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400351 deleteDefaultChannelIfNeededLocked(r);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400352 } catch (PackageManager.NameNotFoundException e) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400353 Slog.e(TAG, "deleteDefaultChannelIfNeededLocked - Exception: " + e);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400354 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400355 }
356 }
357 }
358 }
359 }
360 throw new IllegalStateException("Failed to reach END_DOCUMENT");
361 }
362
Julia Reynolds5c399c62019-04-08 14:42:53 -0400363 private PackagePreferences getPackagePreferencesLocked(String pkg, int uid) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400364 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400365 return mPackagePreferences.get(key);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400366 }
367
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500368 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
369 int uid) {
370 return getOrCreatePackagePreferencesLocked(pkg, UserHandle.getUserId(uid), uid,
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500371 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
Mady Mellora92268c2020-03-09 17:25:08 -0700372 DEFAULT_BUBBLE_PREFERENCE);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400373 }
374
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500375 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
376 @UserIdInt int userId, int uid) {
377 return getOrCreatePackagePreferencesLocked(pkg, userId, uid,
378 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
Mady Mellora92268c2020-03-09 17:25:08 -0700379 DEFAULT_BUBBLE_PREFERENCE);
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500380 }
381
382 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
383 @UserIdInt int userId, int uid, int importance, int priority, int visibility,
Mady Mellora92268c2020-03-09 17:25:08 -0700384 boolean showBadge, int bubblePreference) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400385 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400386 PackagePreferences
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500387 r = (uid == UNKNOWN_UID)
388 ? mRestoredWithoutUids.get(unrestoredPackageKey(pkg, userId))
Julia Reynolds5c399c62019-04-08 14:42:53 -0400389 : mPackagePreferences.get(key);
390 if (r == null) {
391 r = new PackagePreferences();
392 r.pkg = pkg;
393 r.uid = uid;
394 r.importance = importance;
395 r.priority = priority;
396 r.visibility = visibility;
397 r.showBadge = showBadge;
Mady Mellora92268c2020-03-09 17:25:08 -0700398 r.bubblePreference = bubblePreference;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400399
Julia Reynolds5c399c62019-04-08 14:42:53 -0400400 try {
401 createDefaultChannelIfNeededLocked(r);
402 } catch (PackageManager.NameNotFoundException e) {
403 Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400404 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400405
406 if (r.uid == UNKNOWN_UID) {
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500407 mRestoredWithoutUids.put(unrestoredPackageKey(pkg, userId), r);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400408 } else {
409 mPackagePreferences.put(key, r);
410 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400411 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400412 return r;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400413 }
414
415 private boolean shouldHaveDefaultChannel(PackagePreferences r) throws
416 PackageManager.NameNotFoundException {
417 final int userId = UserHandle.getUserId(r.uid);
418 final ApplicationInfo applicationInfo =
419 mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
420 if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
421 // O apps should not have the default channel.
422 return false;
423 }
424
425 // Otherwise, this app should have the default channel.
426 return true;
427 }
428
Julia Reynolds996c7c12019-05-24 10:25:33 -0400429 private boolean deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400430 PackageManager.NameNotFoundException {
431 if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
432 // Not present
Julia Reynolds996c7c12019-05-24 10:25:33 -0400433 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400434 }
435
436 if (shouldHaveDefaultChannel(r)) {
437 // Keep the default channel until upgraded.
Julia Reynolds996c7c12019-05-24 10:25:33 -0400438 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400439 }
440
441 // Remove Default Channel.
442 r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
Julia Reynolds996c7c12019-05-24 10:25:33 -0400443
444 return true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400445 }
446
Julia Reynolds996c7c12019-05-24 10:25:33 -0400447 private boolean createDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400448 PackageManager.NameNotFoundException {
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500449 if (r.uid == UNKNOWN_UID) {
450 return false;
451 }
452
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400453 if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
454 r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
455 com.android.internal.R.string.default_notification_channel_label));
Julia Reynolds996c7c12019-05-24 10:25:33 -0400456 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400457 }
458
459 if (!shouldHaveDefaultChannel(r)) {
460 // Keep the default channel until upgraded.
Julia Reynolds996c7c12019-05-24 10:25:33 -0400461 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400462 }
463
464 // Create Default Channel
465 NotificationChannel channel;
466 channel = new NotificationChannel(
467 NotificationChannel.DEFAULT_CHANNEL_ID,
468 mContext.getString(R.string.default_notification_channel_label),
469 r.importance);
470 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
471 channel.setLockscreenVisibility(r.visibility);
472 if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
473 channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
474 }
475 if (r.priority != DEFAULT_PRIORITY) {
476 channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
477 }
478 if (r.visibility != DEFAULT_VISIBILITY) {
479 channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
480 }
481 r.channels.put(channel.getId(), channel);
Julia Reynolds996c7c12019-05-24 10:25:33 -0400482
483 return true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400484 }
485
Annie Meng8b646fd2019-02-01 18:46:42 +0000486 public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400487 out.startTag(null, TAG_RANKING);
488 out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
Annie Meng8b646fd2019-02-01 18:46:42 +0000489 if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS
490 && (!forBackup || userId == UserHandle.USER_SYSTEM)) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500491 out.startTag(null, TAG_STATUS_ICONS);
492 out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons));
493 out.endTag(null, TAG_STATUS_ICONS);
494 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400495
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400496 synchronized (mPackagePreferences) {
497 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400498 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400499 final PackagePreferences r = mPackagePreferences.valueAt(i);
Annie Meng8b646fd2019-02-01 18:46:42 +0000500 if (forBackup && UserHandle.getUserId(r.uid) != userId) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400501 continue;
502 }
503 final boolean hasNonDefaultSettings =
504 r.importance != DEFAULT_IMPORTANCE
505 || r.priority != DEFAULT_PRIORITY
506 || r.visibility != DEFAULT_VISIBILITY
507 || r.showBadge != DEFAULT_SHOW_BADGE
508 || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
509 || r.channels.size() > 0
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400510 || r.groups.size() > 0
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500511 || r.delegate != null
Julia Reynoldsa7dac432020-04-23 12:17:31 -0400512 || r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400513 || r.hasSentInvalidMessage
514 || r.userDemotedMsgApp
515 || r.hasSentValidMessage;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400516 if (hasNonDefaultSettings) {
517 out.startTag(null, TAG_PACKAGE);
518 out.attribute(null, ATT_NAME, r.pkg);
519 if (r.importance != DEFAULT_IMPORTANCE) {
520 out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
521 }
522 if (r.priority != DEFAULT_PRIORITY) {
523 out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
524 }
525 if (r.visibility != DEFAULT_VISIBILITY) {
526 out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
527 }
Mady Mellora92268c2020-03-09 17:25:08 -0700528 if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) {
529 out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(r.bubblePreference));
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500530 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400531 out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
532 out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
533 Integer.toString(r.lockedAppFields));
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400534 out.attribute(null, ATT_SENT_INVALID_MESSAGE,
535 Boolean.toString(r.hasSentInvalidMessage));
536 out.attribute(null, ATT_SENT_VALID_MESSAGE,
537 Boolean.toString(r.hasSentValidMessage));
538 out.attribute(null, ATT_USER_DEMOTED_INVALID_MSG_APP,
539 Boolean.toString(r.userDemotedMsgApp));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400540
541 if (!forBackup) {
542 out.attribute(null, ATT_UID, Integer.toString(r.uid));
543 }
544
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400545 if (r.delegate != null) {
546 out.startTag(null, TAG_DELEGATE);
547
548 out.attribute(null, ATT_NAME, r.delegate.mPkg);
549 out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
550 if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
551 out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
552 }
553 if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
554 out.attribute(null, ATT_USER_ALLOWED,
555 Boolean.toString(r.delegate.mUserAllowed));
556 }
557 out.endTag(null, TAG_DELEGATE);
558 }
559
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400560 for (NotificationChannelGroup group : r.groups.values()) {
561 group.writeXml(out);
562 }
563
564 for (NotificationChannel channel : r.channels.values()) {
565 if (forBackup) {
566 if (!channel.isDeleted()) {
567 channel.writeXmlForBackup(out, mContext);
568 }
569 } else {
570 channel.writeXml(out);
571 }
572 }
573
574 out.endTag(null, TAG_PACKAGE);
575 }
576 }
577 }
578 out.endTag(null, TAG_RANKING);
579 }
580
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800581 /**
582 * Sets whether bubbles are allowed.
583 *
584 * @param pkg the package to allow or not allow bubbles for.
585 * @param uid the uid to allow or not allow bubbles for.
Mady Mellora92268c2020-03-09 17:25:08 -0700586 * @param bubblePreference whether bubbles are allowed.
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800587 */
Mady Mellora92268c2020-03-09 17:25:08 -0700588 public void setBubblesAllowed(String pkg, int uid, int bubblePreference) {
Mady Mellor9f296142019-05-24 09:42:52 -0700589 boolean changed = false;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400590 synchronized (mPackagePreferences) {
591 PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid);
Mady Mellora92268c2020-03-09 17:25:08 -0700592 changed = p.bubblePreference != bubblePreference;
593 p.bubblePreference = bubblePreference;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400594 p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
595 }
Mady Mellor9f296142019-05-24 09:42:52 -0700596 if (changed) {
597 updateConfig();
598 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500599 }
600
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800601 /**
602 * Whether bubbles are allowed.
603 *
604 * @param pkg the package to check if bubbles are allowed for
605 * @param uid the uid to check if bubbles are allowed for.
606 * @return whether bubbles are allowed.
607 */
Julia Reynolds4509ce72019-01-31 13:12:43 -0500608 @Override
Mady Mellora92268c2020-03-09 17:25:08 -0700609 public int getBubblePreference(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400610 synchronized (mPackagePreferences) {
Mady Mellora92268c2020-03-09 17:25:08 -0700611 return getOrCreatePackagePreferencesLocked(pkg, uid).bubblePreference;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400612 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500613 }
614
615 public int getAppLockedFields(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400616 synchronized (mPackagePreferences) {
617 return getOrCreatePackagePreferencesLocked(pkg, uid).lockedAppFields;
618 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500619 }
620
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400621 /**
622 * Gets importance.
623 */
624 @Override
625 public int getImportance(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400626 synchronized (mPackagePreferences) {
627 return getOrCreatePackagePreferencesLocked(packageName, uid).importance;
628 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400629 }
630
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400631 /**
632 * Returns whether the importance of the corresponding notification is user-locked and shouldn't
633 * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
634 * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
635 */
636 public boolean getIsAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400637 synchronized (mPackagePreferences) {
638 int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields;
639 return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
640 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400641 }
642
643 @Override
644 public boolean canShowBadge(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400645 synchronized (mPackagePreferences) {
646 return getOrCreatePackagePreferencesLocked(packageName, uid).showBadge;
647 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400648 }
649
650 @Override
651 public void setShowBadge(String packageName, int uid, boolean showBadge) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400652 synchronized (mPackagePreferences) {
653 getOrCreatePackagePreferencesLocked(packageName, uid).showBadge = showBadge;
654 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400655 updateConfig();
656 }
657
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400658 public boolean isInInvalidMsgState(String packageName, int uid) {
Julia Reynoldsa7dac432020-04-23 12:17:31 -0400659 synchronized (mPackagePreferences) {
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400660 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
661 return r.hasSentInvalidMessage && !r.hasSentValidMessage;
Julia Reynoldsa7dac432020-04-23 12:17:31 -0400662 }
663 }
664
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400665 public boolean hasUserDemotedInvalidMsgApp(String packageName, int uid) {
Julia Reynoldsa7dac432020-04-23 12:17:31 -0400666 synchronized (mPackagePreferences) {
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -0400667 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
668 return isInInvalidMsgState(packageName, uid) ? r.userDemotedMsgApp : false;
669 }
670 }
671
672 public void setInvalidMsgAppDemoted(String packageName, int uid, boolean isDemoted) {
673 synchronized (mPackagePreferences) {
674 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
675 r.userDemotedMsgApp = isDemoted;
676 }
677 }
678
679 public boolean setInvalidMessageSent(String packageName, int uid) {
680 synchronized (mPackagePreferences) {
681 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
682 boolean valueChanged = r.hasSentInvalidMessage == false;
683 r.hasSentInvalidMessage = true;
684
685 return valueChanged;
686 }
687 }
688
689 public boolean setValidMessageSent(String packageName, int uid) {
690 synchronized (mPackagePreferences) {
691 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
692 boolean valueChanged = r.hasSentValidMessage == false;
693 r.hasSentValidMessage = true;
694
695 return valueChanged;
696 }
697 }
698
699 @VisibleForTesting
700 boolean hasSentInvalidMsg(String packageName, int uid) {
701 synchronized (mPackagePreferences) {
702 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
703 return r.hasSentInvalidMessage;
704 }
705 }
706
707 @VisibleForTesting
708 boolean hasSentValidMsg(String packageName, int uid) {
709 synchronized (mPackagePreferences) {
710 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
711 return r.hasSentValidMessage;
712 }
713 }
714
715 @VisibleForTesting
716 boolean didUserEverDemoteInvalidMsgApp(String packageName, int uid) {
717 synchronized (mPackagePreferences) {
718 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
719 return r.userDemotedMsgApp;
Julia Reynoldsa7dac432020-04-23 12:17:31 -0400720 }
721 }
722
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400723 @Override
724 public boolean isGroupBlocked(String packageName, int uid, String groupId) {
725 if (groupId == null) {
726 return false;
727 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400728 synchronized (mPackagePreferences) {
729 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
730 NotificationChannelGroup group = r.groups.get(groupId);
731 if (group == null) {
732 return false;
733 }
734 return group.isBlocked();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400735 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400736 }
737
738 int getPackagePriority(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400739 synchronized (mPackagePreferences) {
740 return getOrCreatePackagePreferencesLocked(pkg, uid).priority;
741 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400742 }
743
744 int getPackageVisibility(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400745 synchronized (mPackagePreferences) {
746 return getOrCreatePackagePreferencesLocked(pkg, uid).visibility;
747 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400748 }
749
750 @Override
751 public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
752 boolean fromTargetApp) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +0000753 Objects.requireNonNull(pkg);
754 Objects.requireNonNull(group);
755 Objects.requireNonNull(group.getId());
756 Objects.requireNonNull(!TextUtils.isEmpty(group.getName()));
Julia Reynolds5c399c62019-04-08 14:42:53 -0400757 synchronized (mPackagePreferences) {
758 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
759 if (r == null) {
760 throw new IllegalArgumentException("Invalid package");
761 }
762 final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400763 if (oldGroup != null) {
764 group.setChannels(oldGroup.getChannels());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400765
Julia Reynolds5c399c62019-04-08 14:42:53 -0400766 // apps can't update the blocked status or app overlay permission
767 if (fromTargetApp) {
768 group.setBlocked(oldGroup.isBlocked());
769 group.unlockFields(group.getUserLockedFields());
770 group.lockFields(oldGroup.getUserLockedFields());
771 } else {
772 // but the system can
773 if (group.isBlocked() != oldGroup.isBlocked()) {
774 group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
775 updateChannelsBypassingDnd(mContext.getUserId());
776 }
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -0400777 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400778 }
Will Brockman23db6d42020-02-28 09:51:12 -0500779 if (!group.equals(oldGroup)) {
780 // will log for new entries as well as name/description changes
781 MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
782 mNotificationChannelLogger.logNotificationChannelGroup(group, uid, pkg,
783 oldGroup == null,
784 (oldGroup != null) && oldGroup.isBlocked());
785 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400786 r.groups.put(group.getId(), group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400787 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400788 }
789
790 @Override
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400791 public boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400792 boolean fromTargetApp, boolean hasDndAccess) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +0000793 Objects.requireNonNull(pkg);
794 Objects.requireNonNull(channel);
795 Objects.requireNonNull(channel.getId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400796 Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
Will Brockman23db6d42020-02-28 09:51:12 -0500797 boolean needsPolicyFileChange = false, wasUndeleted = false;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400798 synchronized (mPackagePreferences) {
799 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
800 if (r == null) {
801 throw new IllegalArgumentException("Invalid package");
802 }
803 if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
804 throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
805 }
806 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
807 throw new IllegalArgumentException("Reserved id");
808 }
809 NotificationChannel existing = r.channels.get(channel.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400810 if (existing != null && fromTargetApp) {
Will Brockman23db6d42020-02-28 09:51:12 -0500811 // Actually modifying an existing channel - keep most of the existing settings
Julia Reynolds5c399c62019-04-08 14:42:53 -0400812 if (existing.isDeleted()) {
Will Brockman23db6d42020-02-28 09:51:12 -0500813 // The existing channel was deleted - undelete it.
Julia Reynolds5c399c62019-04-08 14:42:53 -0400814 existing.setDeleted(false);
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400815 needsPolicyFileChange = true;
Will Brockman23db6d42020-02-28 09:51:12 -0500816 wasUndeleted = true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400817
Julia Reynolds5c399c62019-04-08 14:42:53 -0400818 // log a resurrected channel as if it's new again
819 MetricsLogger.action(getChannelLog(channel, pkg).setType(
820 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
Will Brockman23db6d42020-02-28 09:51:12 -0500821 mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400822 }
823
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400824 if (!Objects.equals(channel.getName().toString(), existing.getName().toString())) {
825 existing.setName(channel.getName().toString());
826 needsPolicyFileChange = true;
827 }
828 if (!Objects.equals(channel.getDescription(), existing.getDescription())) {
829 existing.setDescription(channel.getDescription());
830 needsPolicyFileChange = true;
831 }
Hall Liu9866aa82020-03-12 12:55:50 -0700832 if (channel.isBlockable() != existing.isBlockable()) {
833 existing.setBlockable(channel.isBlockable());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400834 needsPolicyFileChange = true;
835 }
836 if (channel.getGroup() != null && existing.getGroup() == null) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400837 existing.setGroup(channel.getGroup());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400838 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400839 }
840
841 // Apps are allowed to downgrade channel importance if the user has not changed any
842 // fields on this channel yet.
843 final int previousExistingImportance = existing.getImportance();
Will Brockman3ec09342020-06-19 09:11:06 -0400844 final int previousLoggingImportance =
845 NotificationChannelLogger.getLoggingImportance(existing);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400846 if (existing.getUserLockedFields() == 0 &&
847 channel.getImportance() < existing.getImportance()) {
848 existing.setImportance(channel.getImportance());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400849 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400850 }
851
852 // system apps and dnd access apps can bypass dnd if the user hasn't changed any
853 // fields on the channel yet
854 if (existing.getUserLockedFields() == 0 && hasDndAccess) {
855 boolean bypassDnd = channel.canBypassDnd();
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400856 if (bypassDnd != existing.canBypassDnd()) {
857 existing.setBypassDnd(bypassDnd);
858 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400859
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400860 if (bypassDnd != mAreChannelsBypassingDnd
861 || previousExistingImportance != existing.getImportance()) {
862 updateChannelsBypassingDnd(mContext.getUserId());
863 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400864 }
865 }
866
Julia Reynolds469144c2019-06-21 14:30:28 -0400867 if (existing.getOriginalImportance() == IMPORTANCE_UNSPECIFIED) {
868 existing.setOriginalImportance(channel.getImportance());
869 needsPolicyFileChange = true;
870 }
871
Julia Reynolds5c399c62019-04-08 14:42:53 -0400872 updateConfig();
Will Brockman23db6d42020-02-28 09:51:12 -0500873 if (needsPolicyFileChange && !wasUndeleted) {
874 mNotificationChannelLogger.logNotificationChannelModified(existing, uid, pkg,
Will Brockman3ec09342020-06-19 09:11:06 -0400875 previousLoggingImportance, false);
Will Brockman23db6d42020-02-28 09:51:12 -0500876 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400877 return needsPolicyFileChange;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400878 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400879
Julia Reynoldsc29370a2019-08-20 16:08:42 -0400880 if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
881 throw new IllegalStateException("Limit exceed; cannot create more channels");
882 }
883
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400884 needsPolicyFileChange = true;
885
Julia Reynolds5c399c62019-04-08 14:42:53 -0400886 if (channel.getImportance() < IMPORTANCE_NONE
887 || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
888 throw new IllegalArgumentException("Invalid importance level");
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400889 }
890
Julia Reynolds5c399c62019-04-08 14:42:53 -0400891 // Reset fields that apps aren't allowed to set.
892 if (fromTargetApp && !hasDndAccess) {
893 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400894 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400895 if (fromTargetApp) {
896 channel.setLockscreenVisibility(r.visibility);
Mady Melloree8d5b59e2020-04-09 14:19:53 -0700897 channel.setAllowBubbles(existing != null
898 ? existing.getAllowBubbles()
899 : NotificationChannel.DEFAULT_ALLOW_BUBBLE);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400900 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400901 clearLockedFieldsLocked(channel);
902 channel.setImportanceLockedByOEM(r.oemLockedImportance);
903 if (!channel.isImportanceLockedByOEM()) {
Julia Reynolds72b28442019-11-12 11:43:39 -0500904 if (r.oemLockedChannels.contains(channel.getId())) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400905 channel.setImportanceLockedByOEM(true);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400906 }
907 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400908 channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
909 if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
910 channel.setLockscreenVisibility(
911 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
Julia Reynolds413ba842019-01-11 10:38:08 -0500912 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400913 if (!r.showBadge) {
914 channel.setShowBadge(false);
915 }
Julia Reynolds469144c2019-06-21 14:30:28 -0400916 channel.setOriginalImportance(channel.getImportance());
Julia Reynolds0f767342019-12-18 09:11:55 -0500917
918 // validate parent
919 if (channel.getParentChannelId() != null) {
920 Preconditions.checkArgument(r.channels.containsKey(channel.getParentChannelId()),
921 "Tried to create a conversation channel without a preexisting parent");
922 }
923
Julia Reynolds5c399c62019-04-08 14:42:53 -0400924 r.channels.put(channel.getId(), channel);
925 if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
926 updateChannelsBypassingDnd(mContext.getUserId());
927 }
928 MetricsLogger.action(getChannelLog(channel, pkg).setType(
929 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
Will Brockman23db6d42020-02-28 09:51:12 -0500930 mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400931 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400932
933 return needsPolicyFileChange;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400934 }
935
Julia Reynolds5c399c62019-04-08 14:42:53 -0400936 void clearLockedFieldsLocked(NotificationChannel channel) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400937 channel.unlockFields(channel.getUserLockedFields());
938 }
939
940 @Override
941 public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
942 boolean fromUser) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +0000943 Objects.requireNonNull(updatedChannel);
944 Objects.requireNonNull(updatedChannel.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400945 synchronized (mPackagePreferences) {
946 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
947 if (r == null) {
948 throw new IllegalArgumentException("Invalid package");
949 }
950 NotificationChannel channel = r.channels.get(updatedChannel.getId());
951 if (channel == null || channel.isDeleted()) {
952 throw new IllegalArgumentException("Channel does not exist");
953 }
954 if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
955 updatedChannel.setLockscreenVisibility(
956 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
957 }
958 if (fromUser) {
959 updatedChannel.lockFields(channel.getUserLockedFields());
960 lockFieldsForUpdateLocked(channel, updatedChannel);
961 } else {
962 updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
963 }
964 // no importance updates are allowed if OEM blocked it
965 updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
966 if (updatedChannel.isImportanceLockedByOEM()) {
967 updatedChannel.setImportance(channel.getImportance());
968 }
969 updatedChannel.setImportanceLockedByCriticalDeviceFunction(
970 r.defaultAppLockedImportance);
Beverly47679222019-05-16 15:46:11 -0400971 if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
972 && updatedChannel.getImportance() == IMPORTANCE_NONE) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400973 updatedChannel.setImportance(channel.getImportance());
974 }
Julia Reynolds413ba842019-01-11 10:38:08 -0500975
Julia Reynolds5c399c62019-04-08 14:42:53 -0400976 r.channels.put(updatedChannel.getId(), updatedChannel);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400977
Julia Reynolds5c399c62019-04-08 14:42:53 -0400978 if (onlyHasDefaultChannel(pkg, uid)) {
979 // copy settings to app level so they are inherited by new channels
980 // when the app migrates
981 r.importance = updatedChannel.getImportance();
982 r.priority = updatedChannel.canBypassDnd()
983 ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
984 r.visibility = updatedChannel.getLockscreenVisibility();
985 r.showBadge = updatedChannel.canShowBadge();
986 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400987
Julia Reynolds5c399c62019-04-08 14:42:53 -0400988 if (!channel.equals(updatedChannel)) {
989 // only log if there are real changes
990 MetricsLogger.action(getChannelLog(updatedChannel, pkg)
991 .setSubtype(fromUser ? 1 : 0));
Will Brockman23db6d42020-02-28 09:51:12 -0500992 mNotificationChannelLogger.logNotificationChannelModified(updatedChannel, uid, pkg,
Will Brockman3ec09342020-06-19 09:11:06 -0400993 NotificationChannelLogger.getLoggingImportance(channel), fromUser);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400994 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400995
Julia Reynolds5c399c62019-04-08 14:42:53 -0400996 if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
997 || channel.getImportance() != updatedChannel.getImportance()) {
998 updateChannelsBypassingDnd(mContext.getUserId());
999 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001000 }
1001 updateConfig();
1002 }
1003
1004 @Override
1005 public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
1006 boolean includeDeleted) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001007 Objects.requireNonNull(pkg);
Julia Reynolds12ba4cf2020-01-10 16:01:38 -05001008 return getConversationNotificationChannel(pkg, uid, channelId, null, true, includeDeleted);
Julia Reynolds0f767342019-12-18 09:11:55 -05001009 }
1010
1011 @Override
Julia Reynolds12ba4cf2020-01-10 16:01:38 -05001012 public NotificationChannel getConversationNotificationChannel(String pkg, int uid,
1013 String channelId, String conversationId, boolean returnParentIfNoConversationChannel,
1014 boolean includeDeleted) {
Julia Reynolds0f767342019-12-18 09:11:55 -05001015 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001016 synchronized (mPackagePreferences) {
1017 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1018 if (r == null) {
1019 return null;
1020 }
1021 if (channelId == null) {
1022 channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
1023 }
Julia Reynolds12ba4cf2020-01-10 16:01:38 -05001024 NotificationChannel channel = null;
1025 if (conversationId != null) {
1026 // look for an automatically created conversation specific channel
1027 channel = findConversationChannel(r, channelId, conversationId, includeDeleted);
1028 }
1029 if (channel == null && returnParentIfNoConversationChannel) {
1030 // look for it just based on its id
Julia Reynolds0f767342019-12-18 09:11:55 -05001031 final NotificationChannel nc = r.channels.get(channelId);
1032 if (nc != null && (includeDeleted || !nc.isDeleted())) {
1033 return nc;
1034 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001035 }
Julia Reynolds12ba4cf2020-01-10 16:01:38 -05001036 return channel;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001037 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001038 }
1039
Julia Reynolds0f767342019-12-18 09:11:55 -05001040 private NotificationChannel findConversationChannel(PackagePreferences p, String parentId,
1041 String conversationId, boolean includeDeleted) {
1042 for (NotificationChannel nc : p.channels.values()) {
1043 if (conversationId.equals(nc.getConversationId())
1044 && parentId.equals(nc.getParentChannelId())
1045 && (includeDeleted || !nc.isDeleted())) {
1046 return nc;
1047 }
1048 }
1049 return null;
1050 }
1051
1052 public List<NotificationChannel> getNotificationChannelsByConversationId(String pkg, int uid,
1053 String conversationId) {
1054 Preconditions.checkNotNull(pkg);
1055 Preconditions.checkNotNull(conversationId);
1056 List<NotificationChannel> channels = new ArrayList<>();
1057 synchronized (mPackagePreferences) {
1058 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1059 if (r == null) {
1060 return channels;
1061 }
1062 for (NotificationChannel nc : r.channels.values()) {
1063 if (conversationId.equals(nc.getConversationId())
1064 && !nc.isDeleted()) {
1065 channels.add(nc);
1066 }
1067 }
1068 return channels;
1069 }
1070 }
1071
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001072 @Override
1073 public void deleteNotificationChannel(String pkg, int uid, String channelId) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001074 synchronized (mPackagePreferences) {
1075 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1076 if (r == null) {
1077 return;
1078 }
1079 NotificationChannel channel = r.channels.get(channelId);
1080 if (channel != null) {
Will Brockman23db6d42020-02-28 09:51:12 -05001081 deleteNotificationChannelLocked(channel, pkg, uid);
1082 }
1083 }
1084 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001085
Will Brockman23db6d42020-02-28 09:51:12 -05001086 private void deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
1087 if (!channel.isDeleted()) {
1088 channel.setDeleted(true);
1089 LogMaker lm = getChannelLog(channel, pkg);
1090 lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
1091 MetricsLogger.action(lm);
1092 mNotificationChannelLogger.logNotificationChannelDeleted(channel, uid, pkg);
1093
1094 if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
1095 updateChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001096 }
1097 }
1098 }
1099
1100 @Override
1101 @VisibleForTesting
1102 public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001103 Objects.requireNonNull(pkg);
1104 Objects.requireNonNull(channelId);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001105 synchronized (mPackagePreferences) {
1106 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1107 if (r == null) {
1108 return;
1109 }
1110 r.channels.remove(channelId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001111 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001112 }
1113
1114 @Override
1115 public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001116 Objects.requireNonNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001117 synchronized (mPackagePreferences) {
1118 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1119 if (r == null) {
1120 return;
1121 }
1122 int N = r.channels.size() - 1;
1123 for (int i = N; i >= 0; i--) {
1124 String key = r.channels.keyAt(i);
1125 if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
1126 r.channels.remove(key);
1127 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001128 }
1129 }
1130 }
1131
Julia Reynolds12ad7ca2019-01-28 09:29:16 -05001132 public boolean shouldHideSilentStatusIcons() {
1133 return mHideSilentStatusBarIcons;
1134 }
1135
1136 public void setHideSilentStatusIcons(boolean hide) {
1137 mHideSilentStatusBarIcons = hide;
1138 }
1139
Julia Reynolds413ba842019-01-11 10:38:08 -05001140 public void lockChannelsForOEM(String[] appOrChannelList) {
1141 if (appOrChannelList == null) {
1142 return;
1143 }
1144 for (String appOrChannel : appOrChannelList) {
1145 if (!TextUtils.isEmpty(appOrChannel)) {
1146 String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
1147 if (appSplit != null && appSplit.length > 0) {
1148 String appName = appSplit[0];
1149 String channelId = appSplit.length == 2 ? appSplit[1] : null;
1150
1151 synchronized (mPackagePreferences) {
1152 for (PackagePreferences r : mPackagePreferences.values()) {
1153 if (r.pkg.equals(appName)) {
1154 if (channelId == null) {
1155 // lock all channels for the app
1156 r.oemLockedImportance = true;
1157 for (NotificationChannel channel : r.channels.values()) {
1158 channel.setImportanceLockedByOEM(true);
1159 }
1160 } else {
1161 NotificationChannel channel = r.channels.get(channelId);
1162 if (channel != null) {
1163 channel.setImportanceLockedByOEM(true);
Julia Reynolds413ba842019-01-11 10:38:08 -05001164 }
Julia Reynolds72b28442019-11-12 11:43:39 -05001165 // Also store the locked channels on the record, so they aren't
1166 // temporarily lost when data is cleared on the package
1167 r.oemLockedChannels.add(channelId);
Julia Reynolds413ba842019-01-11 10:38:08 -05001168 }
1169 }
1170 }
1171 }
1172 }
1173 }
1174 }
1175 }
1176
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001177 public void updateDefaultApps(int userId, ArraySet<String> toRemove,
1178 ArraySet<Pair<String, Integer>> toAdd) {
Julia Reynolds0c245002019-03-27 16:10:11 -04001179 synchronized (mPackagePreferences) {
1180 for (PackagePreferences p : mPackagePreferences.values()) {
1181 if (userId == UserHandle.getUserId(p.uid)) {
1182 if (toRemove != null && toRemove.contains(p.pkg)) {
1183 p.defaultAppLockedImportance = false;
1184 for (NotificationChannel channel : p.channels.values()) {
1185 channel.setImportanceLockedByCriticalDeviceFunction(false);
1186 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001187 }
1188 }
1189 }
1190 if (toAdd != null) {
1191 for (Pair<String, Integer> approvedApp : toAdd) {
1192 PackagePreferences p = getOrCreatePackagePreferencesLocked(approvedApp.first,
1193 approvedApp.second);
1194 p.defaultAppLockedImportance = true;
1195 for (NotificationChannel channel : p.channels.values()) {
1196 channel.setImportanceLockedByCriticalDeviceFunction(true);
Julia Reynolds0c245002019-03-27 16:10:11 -04001197 }
1198 }
1199 }
1200 }
1201 }
1202
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001203 public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
1204 int uid, String groupId, boolean includeDeleted) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001205 Objects.requireNonNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001206 synchronized (mPackagePreferences) {
1207 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1208 if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
1209 return null;
1210 }
1211 NotificationChannelGroup group = r.groups.get(groupId).clone();
1212 group.setChannels(new ArrayList<>());
1213 int N = r.channels.size();
1214 for (int i = 0; i < N; i++) {
1215 final NotificationChannel nc = r.channels.valueAt(i);
1216 if (includeDeleted || !nc.isDeleted()) {
1217 if (groupId.equals(nc.getGroup())) {
1218 group.addChannel(nc);
1219 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001220 }
1221 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001222 return group;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001223 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001224 }
1225
1226 public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
1227 int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001228 Objects.requireNonNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001229 synchronized (mPackagePreferences) {
1230 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1231 if (r == null) {
1232 return null;
1233 }
1234 return r.groups.get(groupId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001235 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001236 }
1237
1238 @Override
1239 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001240 int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001241 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001242 Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001243 synchronized (mPackagePreferences) {
1244 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1245 if (r == null) {
1246 return ParceledListSlice.emptyList();
1247 }
1248 NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
1249 int N = r.channels.size();
1250 for (int i = 0; i < N; i++) {
1251 final NotificationChannel nc = r.channels.valueAt(i);
1252 if (includeDeleted || !nc.isDeleted()) {
1253 if (nc.getGroup() != null) {
1254 if (r.groups.get(nc.getGroup()) != null) {
1255 NotificationChannelGroup ncg = groups.get(nc.getGroup());
1256 if (ncg == null) {
1257 ncg = r.groups.get(nc.getGroup()).clone();
1258 ncg.setChannels(new ArrayList<>());
1259 groups.put(nc.getGroup(), ncg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001260
Julia Reynolds5c399c62019-04-08 14:42:53 -04001261 }
1262 ncg.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001263 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001264 } else {
1265 nonGrouped.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001266 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001267 }
1268 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001269 if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
1270 groups.put(null, nonGrouped);
1271 }
1272 if (includeEmpty) {
1273 for (NotificationChannelGroup group : r.groups.values()) {
1274 if (!groups.containsKey(group.getId())) {
1275 groups.put(group.getId(), group);
1276 }
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001277 }
1278 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001279 return new ParceledListSlice<>(new ArrayList<>(groups.values()));
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001280 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001281 }
1282
1283 public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
1284 String groupId) {
1285 List<NotificationChannel> deletedChannels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001286 synchronized (mPackagePreferences) {
1287 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1288 if (r == null || TextUtils.isEmpty(groupId)) {
1289 return deletedChannels;
1290 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001291
Will Brockman23db6d42020-02-28 09:51:12 -05001292 NotificationChannelGroup channelGroup = r.groups.remove(groupId);
1293 if (channelGroup != null) {
1294 mNotificationChannelLogger.logNotificationChannelGroupDeleted(channelGroup, uid,
1295 pkg);
1296 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001297
Julia Reynolds5c399c62019-04-08 14:42:53 -04001298 int N = r.channels.size();
1299 for (int i = 0; i < N; i++) {
1300 final NotificationChannel nc = r.channels.valueAt(i);
1301 if (groupId.equals(nc.getGroup())) {
Will Brockman23db6d42020-02-28 09:51:12 -05001302 deleteNotificationChannelLocked(nc, pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001303 deletedChannels.add(nc);
1304 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001305 }
1306 }
1307 return deletedChannels;
1308 }
1309
1310 @Override
1311 public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
1312 int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001313 List<NotificationChannelGroup> groups = new ArrayList<>();
1314 synchronized (mPackagePreferences) {
1315 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1316 if (r == null) {
1317 return groups;
1318 }
1319 groups.addAll(r.groups.values());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001320 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001321 return groups;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001322 }
1323
Julia Reynolds02971452020-02-14 16:44:19 -05001324 public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
1325 synchronized (mPackagePreferences) {
1326 ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
1327
1328 for (PackagePreferences p : mPackagePreferences.values()) {
1329 int N = p.channels.size();
1330 for (int i = 0; i < N; i++) {
1331 final NotificationChannel nc = p.channels.valueAt(i);
1332 if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()
Julia Reynoldsb1b9d642020-04-22 16:18:44 -04001333 && !nc.isDemoted()
Julia Reynolds02971452020-02-14 16:44:19 -05001334 && (nc.isImportantConversation() || !onlyImportant)) {
1335 ConversationChannelWrapper conversation = new ConversationChannelWrapper();
1336 conversation.setPkg(p.pkg);
1337 conversation.setUid(p.uid);
1338 conversation.setNotificationChannel(nc);
1339 conversation.setParentChannelLabel(
1340 p.channels.get(nc.getParentChannelId()).getName());
1341 boolean blockedByGroup = false;
1342 if (nc.getGroup() != null) {
1343 NotificationChannelGroup group = p.groups.get(nc.getGroup());
1344 if (group != null) {
1345 if (group.isBlocked()) {
1346 blockedByGroup = true;
1347 } else {
1348 conversation.setGroupLabel(group.getName());
1349 }
1350 }
1351 }
1352 if (!blockedByGroup) {
1353 conversations.add(conversation);
1354 }
1355 }
1356 }
1357 }
1358
1359 return conversations;
1360 }
1361 }
1362
Julia Reynolds882f2062020-02-05 12:11:38 -05001363 public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) {
1364 Objects.requireNonNull(pkg);
1365 synchronized (mPackagePreferences) {
1366 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1367 if (r == null) {
1368 return new ArrayList<>();
1369 }
1370 ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
1371 int N = r.channels.size();
1372 for (int i = 0; i < N; i++) {
1373 final NotificationChannel nc = r.channels.valueAt(i);
Julia Reynolds8582df52020-04-24 18:30:59 -04001374 if (!TextUtils.isEmpty(nc.getConversationId())
1375 && !nc.isDeleted()
1376 && !nc.isDemoted()) {
Julia Reynolds882f2062020-02-05 12:11:38 -05001377 ConversationChannelWrapper conversation = new ConversationChannelWrapper();
Julia Reynolds02971452020-02-14 16:44:19 -05001378 conversation.setPkg(r.pkg);
1379 conversation.setUid(r.uid);
Julia Reynolds882f2062020-02-05 12:11:38 -05001380 conversation.setNotificationChannel(nc);
1381 conversation.setParentChannelLabel(
1382 r.channels.get(nc.getParentChannelId()).getName());
1383 boolean blockedByGroup = false;
1384 if (nc.getGroup() != null) {
1385 NotificationChannelGroup group = r.groups.get(nc.getGroup());
1386 if (group != null) {
1387 if (group.isBlocked()) {
1388 blockedByGroup = true;
1389 } else {
1390 conversation.setGroupLabel(group.getName());
1391 }
1392 }
1393 }
1394 if (!blockedByGroup) {
1395 conversations.add(conversation);
1396 }
1397 }
1398 }
1399
1400 return conversations;
1401 }
1402 }
1403
Julia Reynoldsa625b942020-02-15 09:42:23 -05001404 public @NonNull List<String> deleteConversation(String pkg, int uid, String conversationId) {
1405 synchronized (mPackagePreferences) {
1406 List<String> deletedChannelIds = new ArrayList<>();
1407 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1408 if (r == null) {
1409 return deletedChannelIds;
1410 }
1411 int N = r.channels.size();
1412 for (int i = 0; i < N; i++) {
1413 final NotificationChannel nc = r.channels.valueAt(i);
1414 if (conversationId.equals(nc.getConversationId())) {
1415 nc.setDeleted(true);
1416 LogMaker lm = getChannelLog(nc, pkg);
1417 lm.setType(
1418 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
1419 MetricsLogger.action(lm);
Will Brockman23db6d42020-02-28 09:51:12 -05001420 mNotificationChannelLogger.logNotificationChannelDeleted(nc, uid, pkg);
Julia Reynoldsa625b942020-02-15 09:42:23 -05001421
1422 deletedChannelIds.add(nc.getId());
1423 }
1424 }
1425 if (!deletedChannelIds.isEmpty() && mAreChannelsBypassingDnd) {
1426 updateChannelsBypassingDnd(mContext.getUserId());
1427 }
1428 return deletedChannelIds;
1429 }
1430 }
1431
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001432 @Override
1433 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
1434 boolean includeDeleted) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001435 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001436 List<NotificationChannel> channels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001437 synchronized (mPackagePreferences) {
1438 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1439 if (r == null) {
1440 return ParceledListSlice.emptyList();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001441 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001442 int N = r.channels.size();
1443 for (int i = 0; i < N; i++) {
1444 final NotificationChannel nc = r.channels.valueAt(i);
1445 if (includeDeleted || !nc.isDeleted()) {
1446 channels.add(nc);
1447 }
1448 }
1449 return new ParceledListSlice<>(channels);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001450 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001451 }
1452
1453 /**
Beverly0479cde22018-11-09 11:05:34 -05001454 * Gets all notification channels associated with the given pkg and userId that can bypass dnd
1455 */
1456 public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
1457 int userId) {
1458 List<NotificationChannel> channels = new ArrayList<>();
1459 synchronized (mPackagePreferences) {
1460 final PackagePreferences r = mPackagePreferences.get(
1461 packagePreferencesKey(pkg, userId));
1462 // notifications from this package aren't blocked
1463 if (r != null && r.importance != IMPORTANCE_NONE) {
1464 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001465 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001466 channels.add(channel);
1467 }
1468 }
1469 }
1470 }
1471 return new ParceledListSlice<>(channels);
1472 }
1473
1474 /**
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001475 * True for pre-O apps that only have the default channel, or pre O apps that have no
1476 * channels yet. This method will create the default channel for pre-O apps that don't have it.
1477 * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
1478 * upgrades.
1479 */
1480 public boolean onlyHasDefaultChannel(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001481 synchronized (mPackagePreferences) {
1482 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1483 if (r.channels.size() == 1
1484 && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
1485 return true;
1486 }
1487 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001488 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001489 }
1490
1491 public int getDeletedChannelCount(String pkg, int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001492 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001493 int deletedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001494 synchronized (mPackagePreferences) {
1495 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1496 if (r == null) {
1497 return deletedCount;
1498 }
1499 int N = r.channels.size();
1500 for (int i = 0; i < N; i++) {
1501 final NotificationChannel nc = r.channels.valueAt(i);
1502 if (nc.isDeleted()) {
1503 deletedCount++;
1504 }
1505 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001506 return deletedCount;
1507 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001508 }
1509
1510 public int getBlockedChannelCount(String pkg, int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001511 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001512 int blockedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001513 synchronized (mPackagePreferences) {
1514 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1515 if (r == null) {
1516 return blockedCount;
1517 }
1518 int N = r.channels.size();
1519 for (int i = 0; i < N; i++) {
1520 final NotificationChannel nc = r.channels.valueAt(i);
1521 if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
1522 blockedCount++;
1523 }
1524 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001525 return blockedCount;
1526 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001527 }
1528
1529 public int getBlockedAppCount(int userId) {
1530 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001531 synchronized (mPackagePreferences) {
1532 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001533 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001534 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001535 if (userId == UserHandle.getUserId(r.uid)
1536 && r.importance == IMPORTANCE_NONE) {
1537 count++;
1538 }
1539 }
1540 }
1541 return count;
1542 }
1543
Beverly0479cde22018-11-09 11:05:34 -05001544 /**
1545 * Returns the number of apps that have at least one notification channel that can bypass DND
1546 * for given particular user
1547 */
1548 public int getAppsBypassingDndCount(int userId) {
1549 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001550 synchronized (mPackagePreferences) {
Beverly0479cde22018-11-09 11:05:34 -05001551 final int numPackagePreferences = mPackagePreferences.size();
1552 for (int i = 0; i < numPackagePreferences; i++) {
1553 final PackagePreferences r = mPackagePreferences.valueAt(i);
1554 // Package isn't associated with this userId or notifications from this package are
1555 // blocked
1556 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1557 continue;
1558 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001559
Beverly0479cde22018-11-09 11:05:34 -05001560 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001561 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001562 count++;
1563 break;
1564 }
1565 }
1566 }
1567 }
1568 return count;
1569 }
1570
1571 /**
1572 * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
1573 * updating
1574 * @param userId
1575 */
1576 private void syncChannelsBypassingDnd(int userId) {
1577 mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
1578 & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
1579 updateChannelsBypassingDnd(userId);
1580 }
1581
1582 /**
1583 * Updates the user's NotificationPolicy based on whether the given userId
1584 * has channels bypassing DND
1585 * @param userId
1586 */
1587 private void updateChannelsBypassingDnd(int userId) {
1588 synchronized (mPackagePreferences) {
1589 final int numPackagePreferences = mPackagePreferences.size();
1590 for (int i = 0; i < numPackagePreferences; i++) {
1591 final PackagePreferences r = mPackagePreferences.valueAt(i);
1592 // Package isn't associated with this userId or notifications from this package are
1593 // blocked
1594 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1595 continue;
1596 }
1597
1598 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001599 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001600 if (!mAreChannelsBypassingDnd) {
1601 mAreChannelsBypassingDnd = true;
1602 updateZenPolicy(true);
1603 }
1604 return;
1605 }
1606 }
1607 }
1608 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001609 // If no channels bypass DND, update the zen policy once to disable DND bypass.
1610 if (mAreChannelsBypassingDnd) {
1611 mAreChannelsBypassingDnd = false;
1612 updateZenPolicy(false);
1613 }
1614 }
1615
Julia Reynolds5c399c62019-04-08 14:42:53 -04001616 private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
Beverly0479cde22018-11-09 11:05:34 -05001617 // Channel is in a group that's blocked
Beverly4f7b53d2018-11-20 09:56:31 -05001618 if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
1619 return false;
Beverly0479cde22018-11-09 11:05:34 -05001620 }
1621
1622 // Channel is deleted or is blocked
1623 if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
1624 return false;
1625 }
1626
1627 return true;
1628 }
1629
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001630 public void updateZenPolicy(boolean areChannelsBypassingDnd) {
1631 NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
1632 mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
1633 policy.priorityCategories, policy.priorityCallSenders,
1634 policy.priorityMessageSenders, policy.suppressedVisualEffects,
1635 (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
Julia Reynolds24edc002020-01-29 16:35:32 -05001636 : 0),
1637 policy.priorityConversationSenders));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001638 }
1639
1640 public boolean areChannelsBypassingDnd() {
1641 return mAreChannelsBypassingDnd;
1642 }
1643
1644 /**
1645 * Sets importance.
1646 */
1647 @Override
1648 public void setImportance(String pkgName, int uid, int importance) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001649 synchronized (mPackagePreferences) {
1650 getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance;
1651 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001652 updateConfig();
1653 }
1654
1655 public void setEnabled(String packageName, int uid, boolean enabled) {
1656 boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
1657 if (wasEnabled == enabled) {
1658 return;
1659 }
1660 setImportance(packageName, uid,
1661 enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
Will Brockman7575e052020-06-16 15:05:12 -04001662 mNotificationChannelLogger.logAppNotificationsAllowed(uid, packageName, enabled);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001663 }
1664
1665 /**
1666 * Sets whether any notifications from the app, represented by the given {@code pkgName} and
1667 * {@code uid}, have their importance locked by the user. Locked notifications don't get
1668 * considered for sentiment adjustments (and thus never show a blocking helper).
1669 */
1670 public void setAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001671 synchronized (mPackagePreferences) {
1672 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(packageName, uid);
1673 if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
1674 return;
1675 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001676
Julia Reynolds5c399c62019-04-08 14:42:53 -04001677 prefs.lockedAppFields =
1678 prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
1679 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001680 updateConfig();
1681 }
1682
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001683 /**
1684 * Returns the delegate for a given package, if it's allowed by the package and the user.
1685 */
1686 public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001687 synchronized (mPackagePreferences) {
1688 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001689
Julia Reynolds5c399c62019-04-08 14:42:53 -04001690 if (prefs == null || prefs.delegate == null) {
1691 return null;
1692 }
1693 if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
1694 return null;
1695 }
1696 return prefs.delegate.mPkg;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001697 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001698 }
1699
1700 /**
1701 * Used by an app to delegate notification posting privileges to another apps.
1702 */
1703 public void setNotificationDelegate(String sourcePkg, int sourceUid,
1704 String delegatePkg, int delegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001705 synchronized (mPackagePreferences) {
1706 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001707
Julia Reynolds5c399c62019-04-08 14:42:53 -04001708 boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
1709 Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
1710 prefs.delegate = delegate;
1711 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001712 updateConfig();
1713 }
1714
1715 /**
1716 * Used by an app to turn off its notification delegate.
1717 */
1718 public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001719 boolean changed = false;
1720 synchronized (mPackagePreferences) {
1721 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1722 if (prefs != null && prefs.delegate != null) {
1723 prefs.delegate.mEnabled = false;
1724 changed = true;
1725 }
1726 }
1727 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001728 updateConfig();
1729 }
1730 }
1731
1732 /**
1733 * Toggles whether an app can have a notification delegate on behalf of a user.
1734 */
1735 public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001736 boolean changed = false;
1737 synchronized (mPackagePreferences) {
1738 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1739 if (prefs != null && prefs.delegate != null) {
1740 prefs.delegate.mUserAllowed = userAllowed;
1741 changed = true;
1742 }
1743 }
1744 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001745 updateConfig();
1746 }
1747 }
1748
1749 /**
1750 * Returns whether the given app is allowed on post notifications on behalf of the other given
1751 * app.
1752 */
1753 public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
1754 String potentialDelegatePkg, int potentialDelegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001755 synchronized (mPackagePreferences) {
1756 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001757
Julia Reynolds5c399c62019-04-08 14:42:53 -04001758 return prefs != null && prefs.isValidDelegate(potentialDelegatePkg,
1759 potentialDelegateUid);
1760 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001761 }
1762
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001763 @VisibleForTesting
Julia Reynolds5c399c62019-04-08 14:42:53 -04001764 void lockFieldsForUpdateLocked(NotificationChannel original, NotificationChannel update) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001765 if (original.canBypassDnd() != update.canBypassDnd()) {
1766 update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
1767 }
1768 if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
1769 update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
1770 }
1771 if (original.getImportance() != update.getImportance()) {
1772 update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
1773 }
1774 if (original.shouldShowLights() != update.shouldShowLights()
1775 || original.getLightColor() != update.getLightColor()) {
1776 update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
1777 }
1778 if (!Objects.equals(original.getSound(), update.getSound())) {
1779 update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
1780 }
1781 if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
1782 || original.shouldVibrate() != update.shouldVibrate()) {
1783 update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
1784 }
1785 if (original.canShowBadge() != update.canShowBadge()) {
1786 update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
1787 }
Mady Melloree8d5b59e2020-04-09 14:19:53 -07001788 if (original.getAllowBubbles() != update.getAllowBubbles()) {
Mady Mellorc39b4ae2019-01-09 17:11:37 -08001789 update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE);
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -04001790 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001791 }
1792
1793 public void dump(PrintWriter pw, String prefix,
1794 @NonNull NotificationManagerService.DumpFilter filter) {
1795 pw.print(prefix);
1796 pw.println("per-package config:");
1797
Julia Reynolds5c399c62019-04-08 14:42:53 -04001798 pw.println("PackagePreferences:");
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001799 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001800 dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001801 }
1802 pw.println("Restored without uid:");
Julia Reynolds5c399c62019-04-08 14:42:53 -04001803 dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001804 }
1805
1806 public void dump(ProtoOutputStream proto,
1807 @NonNull NotificationManagerService.DumpFilter filter) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001808 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001809 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001810 mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001811 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001812 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001813 mRestoredWithoutUids);
1814 }
1815
Julia Reynolds5c399c62019-04-08 14:42:53 -04001816 private static void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001817 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001818 ArrayMap<String, PackagePreferences> packagePreferences) {
1819 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001820 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001821 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001822 if (filter.matches(r.pkg)) {
1823 pw.print(prefix);
1824 pw.print(" AppSettings: ");
1825 pw.print(r.pkg);
1826 pw.print(" (");
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001827 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001828 pw.print(')');
1829 if (r.importance != DEFAULT_IMPORTANCE) {
1830 pw.print(" importance=");
1831 pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
1832 }
1833 if (r.priority != DEFAULT_PRIORITY) {
1834 pw.print(" priority=");
1835 pw.print(Notification.priorityToString(r.priority));
1836 }
1837 if (r.visibility != DEFAULT_VISIBILITY) {
1838 pw.print(" visibility=");
1839 pw.print(Notification.visibilityToString(r.visibility));
1840 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001841 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1842 pw.print(" showBadge=");
1843 pw.print(r.showBadge);
1844 }
1845 if (r.defaultAppLockedImportance != DEFAULT_APP_LOCKED_IMPORTANCE) {
1846 pw.print(" defaultAppLocked=");
1847 pw.print(r.defaultAppLockedImportance);
1848 }
1849 if (r.oemLockedImportance != DEFAULT_OEM_LOCKED_IMPORTANCE) {
1850 pw.print(" oemLocked=");
1851 pw.print(r.oemLockedImportance);
1852 }
Julia Reynolds72b28442019-11-12 11:43:39 -05001853 if (!r.oemLockedChannels.isEmpty()) {
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001854 pw.print(" futureLockedChannels=");
Julia Reynolds72b28442019-11-12 11:43:39 -05001855 pw.print(r.oemLockedChannels);
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001856 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001857 pw.println();
1858 for (NotificationChannel channel : r.channels.values()) {
1859 pw.print(prefix);
1860 channel.dump(pw, " ", filter.redact);
1861 }
1862 for (NotificationChannelGroup group : r.groups.values()) {
1863 pw.print(prefix);
1864 pw.print(" ");
1865 pw.print(" ");
1866 pw.println(group);
1867 }
1868 }
1869 }
1870 }
1871
Julia Reynolds5c399c62019-04-08 14:42:53 -04001872 private static void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001873 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001874 ArrayMap<String, PackagePreferences> packagePreferences) {
1875 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001876 long fToken;
1877 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001878 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001879 if (filter.matches(r.pkg)) {
1880 fToken = proto.start(fieldId);
1881
1882 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
1883 proto.write(RankingHelperProto.RecordProto.UID, r.uid);
1884 proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
1885 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
1886 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
1887 proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
1888
1889 for (NotificationChannel channel : r.channels.values()) {
Jeffrey Huangcb782852019-12-05 11:28:11 -08001890 channel.dumpDebug(proto, RankingHelperProto.RecordProto.CHANNELS);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001891 }
1892 for (NotificationChannelGroup group : r.groups.values()) {
Jeffrey Huangcb782852019-12-05 11:28:11 -08001893 group.dumpDebug(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001894 }
1895
1896 proto.end(fToken);
1897 }
1898 }
1899 }
1900
Yotam Aron74299972020-01-16 16:20:58 +02001901 /**
1902 * Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}.
1903 */
1904 public void pullPackagePreferencesStats(List<StatsEvent> events) {
1905 synchronized (mPackagePreferences) {
1906 for (int i = 0; i < mPackagePreferences.size(); i++) {
1907 if (i > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
1908 break;
1909 }
Chris Wren1a934a32020-05-19 13:45:46 -04001910 SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
Yotam Aron74299972020-01-16 16:20:58 +02001911 .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
1912 final PackagePreferences r = mPackagePreferences.valueAt(i);
1913 event.writeInt(r.uid);
Muhammad Qureshi22e52da2020-03-30 21:14:21 -07001914 event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
Yotam Aron74299972020-01-16 16:20:58 +02001915 event.writeInt(r.importance);
1916 event.writeInt(r.visibility);
1917 event.writeInt(r.lockedAppFields);
1918 events.add(event.build());
1919 }
1920 }
1921 }
1922
1923 /**
1924 * Fills out {@link PackageNotificationChannelPreferences} proto and wraps it in a
1925 * {@link StatsEvent}.
1926 */
1927 public void pullPackageChannelPreferencesStats(List<StatsEvent> events) {
1928 synchronized (mPackagePreferences) {
1929 int totalChannelsPulled = 0;
1930 for (int i = 0; i < mPackagePreferences.size(); i++) {
1931 if (totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
1932 break;
1933 }
1934 final PackagePreferences r = mPackagePreferences.valueAt(i);
1935 for (NotificationChannel channel : r.channels.values()) {
1936 if (++totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
1937 break;
1938 }
Chris Wren1a934a32020-05-19 13:45:46 -04001939 SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
Yotam Aron74299972020-01-16 16:20:58 +02001940 .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
1941 event.writeInt(r.uid);
Muhammad Qureshi22e52da2020-03-30 21:14:21 -07001942 event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
Yotam Aron74299972020-01-16 16:20:58 +02001943 event.writeString(channel.getId());
1944 event.writeString(channel.getName().toString());
1945 event.writeString(channel.getDescription());
1946 event.writeInt(channel.getImportance());
1947 event.writeInt(channel.getUserLockedFields());
1948 event.writeBoolean(channel.isDeleted());
Chris Wren07cb6a42020-05-14 17:35:30 -04001949 event.writeBoolean(channel.getConversationId() != null);
1950 event.writeBoolean(channel.isDemoted());
1951 event.writeBoolean(channel.isImportantConversation());
Yotam Aron74299972020-01-16 16:20:58 +02001952 events.add(event.build());
1953 }
1954 }
1955 }
1956 }
1957
1958 /**
1959 * Fills out {@link PackageNotificationChannelGroupPreferences} proto and wraps it in a
1960 * {@link StatsEvent}.
1961 */
1962 public void pullPackageChannelGroupPreferencesStats(List<StatsEvent> events) {
1963 synchronized (mPackagePreferences) {
1964 int totalGroupsPulled = 0;
1965 for (int i = 0; i < mPackagePreferences.size(); i++) {
1966 if (totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
1967 break;
1968 }
1969 final PackagePreferences r = mPackagePreferences.valueAt(i);
1970 for (NotificationChannelGroup groupChannel : r.groups.values()) {
1971 if (++totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
1972 break;
1973 }
Chris Wren1a934a32020-05-19 13:45:46 -04001974 SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
Yotam Aron74299972020-01-16 16:20:58 +02001975 .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
1976 event.writeInt(r.uid);
Muhammad Qureshi22e52da2020-03-30 21:14:21 -07001977 event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
Yotam Aron74299972020-01-16 16:20:58 +02001978 event.writeString(groupChannel.getId());
1979 event.writeString(groupChannel.getName().toString());
1980 event.writeString(groupChannel.getDescription());
1981 event.writeBoolean(groupChannel.isBlocked());
1982 event.writeInt(groupChannel.getUserLockedFields());
1983 events.add(event.build());
1984 }
1985 }
1986 }
1987 }
1988
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001989 public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
1990 JSONObject ranking = new JSONObject();
1991 JSONArray PackagePreferencess = new JSONArray();
1992 try {
1993 ranking.put("noUid", mRestoredWithoutUids.size());
1994 } catch (JSONException e) {
1995 // pass
1996 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001997 synchronized (mPackagePreferences) {
1998 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001999 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002000 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002001 if (filter == null || filter.matches(r.pkg)) {
2002 JSONObject PackagePreferences = new JSONObject();
2003 try {
2004 PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
2005 PackagePreferences.put("packageName", r.pkg);
2006 if (r.importance != DEFAULT_IMPORTANCE) {
2007 PackagePreferences.put("importance",
2008 NotificationListenerService.Ranking.importanceToString(
2009 r.importance));
2010 }
2011 if (r.priority != DEFAULT_PRIORITY) {
2012 PackagePreferences.put("priority",
2013 Notification.priorityToString(r.priority));
2014 }
2015 if (r.visibility != DEFAULT_VISIBILITY) {
2016 PackagePreferences.put("visibility",
2017 Notification.visibilityToString(r.visibility));
2018 }
2019 if (r.showBadge != DEFAULT_SHOW_BADGE) {
2020 PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
2021 }
2022 JSONArray channels = new JSONArray();
2023 for (NotificationChannel channel : r.channels.values()) {
2024 channels.put(channel.toJson());
2025 }
2026 PackagePreferences.put("channels", channels);
2027 JSONArray groups = new JSONArray();
2028 for (NotificationChannelGroup group : r.groups.values()) {
2029 groups.put(group.toJson());
2030 }
2031 PackagePreferences.put("groups", groups);
2032 } catch (JSONException e) {
2033 // pass
2034 }
2035 PackagePreferencess.put(PackagePreferences);
2036 }
2037 }
2038 }
2039 try {
2040 ranking.put("PackagePreferencess", PackagePreferencess);
2041 } catch (JSONException e) {
2042 // pass
2043 }
2044 return ranking;
2045 }
2046
2047 /**
2048 * Dump only the ban information as structured JSON for the stats collector.
2049 *
2050 * This is intentionally redundant with {#link dumpJson} because the old
2051 * scraper will expect this format.
2052 *
2053 * @param filter
2054 * @return
2055 */
2056 public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
2057 JSONArray bans = new JSONArray();
2058 Map<Integer, String> packageBans = getPackageBans();
2059 for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
2060 final int userId = UserHandle.getUserId(ban.getKey());
2061 final String packageName = ban.getValue();
2062 if (filter == null || filter.matches(packageName)) {
2063 JSONObject banJson = new JSONObject();
2064 try {
2065 banJson.put("userId", userId);
2066 banJson.put("packageName", packageName);
2067 } catch (JSONException e) {
2068 e.printStackTrace();
2069 }
2070 bans.put(banJson);
2071 }
2072 }
2073 return bans;
2074 }
2075
2076 public Map<Integer, String> getPackageBans() {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002077 synchronized (mPackagePreferences) {
2078 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002079 ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
2080 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002081 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002082 if (r.importance == IMPORTANCE_NONE) {
2083 packageBans.put(r.uid, r.pkg);
2084 }
2085 }
2086
2087 return packageBans;
2088 }
2089 }
2090
2091 /**
2092 * Dump only the channel information as structured JSON for the stats collector.
2093 *
2094 * This is intentionally redundant with {#link dumpJson} because the old
2095 * scraper will expect this format.
2096 *
2097 * @param filter
2098 * @return
2099 */
2100 public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
2101 JSONArray channels = new JSONArray();
2102 Map<String, Integer> packageChannels = getPackageChannels();
2103 for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) {
2104 final String packageName = channelCount.getKey();
2105 if (filter == null || filter.matches(packageName)) {
2106 JSONObject channelCountJson = new JSONObject();
2107 try {
2108 channelCountJson.put("packageName", packageName);
2109 channelCountJson.put("channelCount", channelCount.getValue());
2110 } catch (JSONException e) {
2111 e.printStackTrace();
2112 }
2113 channels.put(channelCountJson);
2114 }
2115 }
2116 return channels;
2117 }
2118
2119 private Map<String, Integer> getPackageChannels() {
2120 ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002121 synchronized (mPackagePreferences) {
2122 for (int i = 0; i < mPackagePreferences.size(); i++) {
2123 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002124 int channelCount = 0;
2125 for (int j = 0; j < r.channels.size(); j++) {
2126 if (!r.channels.valueAt(j).isDeleted()) {
2127 channelCount++;
2128 }
2129 }
2130 packageChannels.put(r.pkg, channelCount);
2131 }
2132 }
2133 return packageChannels;
2134 }
2135
Beverly0479cde22018-11-09 11:05:34 -05002136 /**
2137 * Called when user switches
2138 */
2139 public void onUserSwitched(int userId) {
2140 syncChannelsBypassingDnd(userId);
2141 }
2142
2143 /**
2144 * Called when user is unlocked
2145 */
2146 public void onUserUnlocked(int userId) {
2147 syncChannelsBypassingDnd(userId);
2148 }
2149
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002150 public void onUserRemoved(int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002151 synchronized (mPackagePreferences) {
2152 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002153 for (int i = N - 1; i >= 0; i--) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002154 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002155 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002156 mPackagePreferences.removeAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002157 }
2158 }
2159 }
2160 }
2161
2162 protected void onLocaleChanged(Context context, int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002163 synchronized (mPackagePreferences) {
2164 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002165 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002166 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002167 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
2168 if (PackagePreferences.channels.containsKey(
2169 NotificationChannel.DEFAULT_CHANNEL_ID)) {
2170 PackagePreferences.channels.get(
2171 NotificationChannel.DEFAULT_CHANNEL_ID).setName(
2172 context.getResources().getString(
2173 R.string.default_notification_channel_label));
2174 }
2175 }
2176 }
2177 }
2178 }
2179
Julia Reynolds996c7c12019-05-24 10:25:33 -04002180 public boolean onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002181 int[] uidList) {
2182 if (pkgList == null || pkgList.length == 0) {
Julia Reynolds996c7c12019-05-24 10:25:33 -04002183 return false; // nothing to do
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002184 }
2185 boolean updated = false;
2186 if (removingPackage) {
2187 // Remove notification settings for uninstalled package
2188 int size = Math.min(pkgList.length, uidList.length);
2189 for (int i = 0; i < size; i++) {
2190 final String pkg = pkgList[i];
2191 final int uid = uidList[i];
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002192 synchronized (mPackagePreferences) {
2193 mPackagePreferences.remove(packagePreferencesKey(pkg, uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002194 }
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002195 mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002196 updated = true;
2197 }
2198 } else {
2199 for (String pkg : pkgList) {
2200 // Package install
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002201 final PackagePreferences r =
2202 mRestoredWithoutUids.get(unrestoredPackageKey(pkg, changeUserId));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002203 if (r != null) {
2204 try {
2205 r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002206 mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId));
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002207 synchronized (mPackagePreferences) {
2208 mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002209 }
2210 updated = true;
2211 } catch (PackageManager.NameNotFoundException e) {
2212 // noop
2213 }
2214 }
2215 // Package upgrade
2216 try {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002217 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04002218 PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002219 mPm.getPackageUidAsUser(pkg, changeUserId));
2220 if (fullPackagePreferences != null) {
Julia Reynolds996c7c12019-05-24 10:25:33 -04002221 updated |= createDefaultChannelIfNeededLocked(fullPackagePreferences);
2222 updated |= deleteDefaultChannelIfNeededLocked(fullPackagePreferences);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002223 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002224 }
2225 } catch (PackageManager.NameNotFoundException e) {
2226 }
2227 }
2228 }
2229
2230 if (updated) {
2231 updateConfig();
2232 }
Julia Reynolds996c7c12019-05-24 10:25:33 -04002233 return updated;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002234 }
2235
Julia Reynolds7af51c52019-04-19 11:08:27 -04002236 public void clearData(String pkg, int uid) {
2237 synchronized (mPackagePreferences) {
2238 PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
2239 if (p != null) {
2240 p.channels = new ArrayMap<>();
2241 p.groups = new ArrayMap<>();
2242 p.delegate = null;
2243 p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Mady Mellora92268c2020-03-09 17:25:08 -07002244 p.bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
Julia Reynolds7af51c52019-04-19 11:08:27 -04002245 p.importance = DEFAULT_IMPORTANCE;
2246 p.priority = DEFAULT_PRIORITY;
2247 p.visibility = DEFAULT_VISIBILITY;
2248 p.showBadge = DEFAULT_SHOW_BADGE;
2249 }
2250 }
2251 }
2252
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002253 private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
2254 return new LogMaker(
2255 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2256 .ACTION_NOTIFICATION_CHANNEL)
2257 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
2258 .setPackageName(pkg)
2259 .addTaggedData(
2260 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2261 .FIELD_NOTIFICATION_CHANNEL_ID,
2262 channel.getId())
2263 .addTaggedData(
2264 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2265 .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
2266 channel.getImportance());
2267 }
2268
2269 private LogMaker getChannelGroupLog(String groupId, String pkg) {
2270 return new LogMaker(
2271 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2272 .ACTION_NOTIFICATION_CHANNEL_GROUP)
2273 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
2274 .addTaggedData(
2275 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2276 .FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
2277 groupId)
2278 .setPackageName(pkg);
2279 }
2280
Julia Reynolds4509ce72019-01-31 13:12:43 -05002281 public void updateBubblesEnabled() {
Lyn Han4463f842019-07-09 15:27:28 -07002282 final boolean newValue = Settings.Global.getInt(mContext.getContentResolver(),
2283 Settings.Global.NOTIFICATION_BUBBLES,
Mady Mellora92268c2020-03-09 17:25:08 -07002284 DEFAULT_GLOBAL_ALLOW_BUBBLE ? 1 : 0) == 1;
2285 if (newValue != mBubblesEnabledGlobally) {
2286 mBubblesEnabledGlobally = newValue;
Julia Reynolds4509ce72019-01-31 13:12:43 -05002287 updateConfig();
2288 }
2289 }
2290
Lyn Han4463f842019-07-09 15:27:28 -07002291 public boolean bubblesEnabled() {
Mady Mellora92268c2020-03-09 17:25:08 -07002292 return mBubblesEnabledGlobally;
Julia Reynolds4509ce72019-01-31 13:12:43 -05002293 }
2294
Robert Snoebergerdaf50b52020-06-18 21:40:30 -04002295 /** Requests check of the feature setting for showing media notifications in quick settings. */
2296 public void updateMediaNotificationFilteringEnabled() {
2297 final boolean newValue = Settings.Global.getInt(mContext.getContentResolver(),
2298 Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1) > 0;
2299 if (newValue != mIsMediaNotificationFilteringEnabled) {
2300 mIsMediaNotificationFilteringEnabled = newValue;
2301 updateConfig();
2302 }
2303 }
2304
2305 /** Returns true if the setting is enabled for showing media notifications in quick settings. */
2306 public boolean isMediaNotificationFilteringEnabled() {
2307 return mIsMediaNotificationFilteringEnabled;
2308 }
2309
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002310 public void updateBadgingEnabled() {
2311 if (mBadgingEnabled == null) {
2312 mBadgingEnabled = new SparseBooleanArray();
2313 }
2314 boolean changed = false;
2315 // update the cached values
2316 for (int index = 0; index < mBadgingEnabled.size(); index++) {
2317 int userId = mBadgingEnabled.keyAt(index);
2318 final boolean oldValue = mBadgingEnabled.get(userId);
2319 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2320 Settings.Secure.NOTIFICATION_BADGING,
2321 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
2322 mBadgingEnabled.put(userId, newValue);
2323 changed |= oldValue != newValue;
2324 }
2325 if (changed) {
2326 updateConfig();
2327 }
2328 }
2329
2330 public boolean badgingEnabled(UserHandle userHandle) {
2331 int userId = userHandle.getIdentifier();
2332 if (userId == UserHandle.USER_ALL) {
2333 return false;
2334 }
2335 if (mBadgingEnabled.indexOfKey(userId) < 0) {
2336 mBadgingEnabled.put(userId,
2337 Settings.Secure.getIntForUser(mContext.getContentResolver(),
2338 Settings.Secure.NOTIFICATION_BADGING,
2339 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
2340 }
2341 return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
2342 }
2343
2344 private void updateConfig() {
2345 mRankingHandler.requestSort();
2346 }
2347
2348 private static String packagePreferencesKey(String pkg, int uid) {
2349 return pkg + "|" + uid;
2350 }
2351
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002352 private static String unrestoredPackageKey(String pkg, @UserIdInt int userId) {
2353 return pkg + "|" + userId;
2354 }
2355
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002356 private static class PackagePreferences {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002357 String pkg;
2358 int uid = UNKNOWN_UID;
2359 int importance = DEFAULT_IMPORTANCE;
2360 int priority = DEFAULT_PRIORITY;
2361 int visibility = DEFAULT_VISIBILITY;
2362 boolean showBadge = DEFAULT_SHOW_BADGE;
Mady Mellora92268c2020-03-09 17:25:08 -07002363 int bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002364 int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Julia Reynolds0c245002019-03-27 16:10:11 -04002365 // these fields are loaded on boot from a different source of truth and so are not
2366 // written to notification policy xml
Julia Reynolds413ba842019-01-11 10:38:08 -05002367 boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
Julia Reynolds72b28442019-11-12 11:43:39 -05002368 List<String> oemLockedChannels = new ArrayList<>();
Julia Reynolds0c245002019-03-27 16:10:11 -04002369 boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE;
Julia Reynoldsbc23c7e2020-05-13 18:16:32 -04002370
2371 boolean hasSentInvalidMessage = false;
2372 boolean hasSentValidMessage = false;
2373 // notE: only valid while hasSentMessage is false and hasSentInvalidMessage is true
2374 boolean userDemotedMsgApp = false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002375
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04002376 Delegate delegate = null;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002377 ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
2378 Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04002379
2380 public boolean isValidDelegate(String pkg, int uid) {
2381 return delegate != null && delegate.isAllowed(pkg, uid);
2382 }
2383 }
2384
2385 private static class Delegate {
2386 static final boolean DEFAULT_ENABLED = true;
2387 static final boolean DEFAULT_USER_ALLOWED = true;
2388 String mPkg;
2389 int mUid = UNKNOWN_UID;
2390 boolean mEnabled = DEFAULT_ENABLED;
2391 boolean mUserAllowed = DEFAULT_USER_ALLOWED;
2392
2393 Delegate(String pkg, int uid, boolean enabled, boolean userAllowed) {
2394 mPkg = pkg;
2395 mUid = uid;
2396 mEnabled = enabled;
2397 mUserAllowed = userAllowed;
2398 }
2399
2400 public boolean isAllowed(String pkg, int uid) {
2401 if (pkg == null || uid == UNKNOWN_UID) {
2402 return false;
2403 }
2404 return pkg.equals(mPkg)
2405 && uid == mUid
2406 && (mUserAllowed && mEnabled);
2407 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002408 }
2409}