blob: d432fc83b52ab64e62c7012fc04dd2e14ae2195c [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;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -040088 private static final int UNKNOWN_UID = UserHandle.USER_NULL;
Julia Reynolds413ba842019-01-11 10:38:08 -050089 private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040090
91 @VisibleForTesting
Julia Reynoldsc29370a2019-08-20 16:08:42 -040092 static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
93
Yotam Aron74299972020-01-16 16:20:58 +020094 private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000;
95 private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000;
96 private static final int NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT = 1000;
97
Julia Reynoldsc29370a2019-08-20 16:08:42 -040098 @VisibleForTesting
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040099 static final String TAG_RANKING = "ranking";
100 private static final String TAG_PACKAGE = "package";
101 private static final String TAG_CHANNEL = "channel";
102 private static final String TAG_GROUP = "channelGroup";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400103 private static final String TAG_DELEGATE = "delegate";
Julia Reynolds905df642019-05-31 15:29:59 -0400104 private static final String TAG_STATUS_ICONS = "silent_status_icons";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400105
106 private static final String ATT_VERSION = "version";
107 private static final String ATT_NAME = "name";
108 private static final String ATT_UID = "uid";
109 private static final String ATT_ID = "id";
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800110 private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400111 private static final String ATT_PRIORITY = "priority";
112 private static final String ATT_VISIBILITY = "visibility";
113 private static final String ATT_IMPORTANCE = "importance";
114 private static final String ATT_SHOW_BADGE = "show_badge";
115 private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400116 private static final String ATT_ENABLED = "enabled";
117 private static final String ATT_USER_ALLOWED = "allowed";
Julia Reynolds25692c42019-05-03 15:01:24 -0400118 private static final String ATT_HIDE_SILENT = "hide_gentle";
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400119
120 private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
121 private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
122 private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500123 @VisibleForTesting
Julia Reynolds25692c42019-05-03 15:01:24 -0400124 static final boolean DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS = false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400125 private static final boolean DEFAULT_SHOW_BADGE = true;
Mady Mellora92268c2020-03-09 17:25:08 -0700126
Julia Reynolds413ba842019-01-11 10:38:08 -0500127 private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false;
Julia Reynolds0c245002019-03-27 16:10:11 -0400128 private static final boolean DEFAULT_APP_LOCKED_IMPORTANCE = false;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800129
Mady Mellora92268c2020-03-09 17:25:08 -0700130 static final boolean DEFAULT_GLOBAL_ALLOW_BUBBLE = true;
131 @VisibleForTesting
132 static final int DEFAULT_BUBBLE_PREFERENCE = BUBBLE_PREFERENCE_NONE;
133
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400134 /**
135 * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
136 * fields.
137 */
138 private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
139
140 /**
141 * All user-lockable fields for a given application.
142 */
143 @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
144 public @interface LockableAppFields {
145 int USER_LOCKED_IMPORTANCE = 0x00000001;
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800146 int USER_LOCKED_BUBBLE = 0x00000002;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400147 }
148
149 // pkg|uid => PackagePreferences
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400150 private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>();
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500151 // pkg|userId => PackagePreferences
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400152 private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
153
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400154 private final Context mContext;
155 private final PackageManager mPm;
156 private final RankingHandler mRankingHandler;
157 private final ZenModeHelper mZenModeHelper;
Will Brockman23db6d42020-02-28 09:51:12 -0500158 private final NotificationChannelLogger mNotificationChannelLogger;
Mady Mellorc888d512020-04-09 14:48:02 -0700159 private final AppOpsManager mAppOps;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400160
161 private SparseBooleanArray mBadgingEnabled;
Mady Mellora92268c2020-03-09 17:25:08 -0700162 private boolean mBubblesEnabledGlobally = DEFAULT_GLOBAL_ALLOW_BUBBLE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400163 private boolean mAreChannelsBypassingDnd;
Julia Reynolds2594b472019-04-03 13:30:16 -0400164 private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400165
Julia Reynolds7c267522020-01-16 11:26:41 -0500166 private boolean mAllowInvalidShortcuts = false;
167
Mady Mellor973bc212019-11-01 10:06:49 -0700168 private static final String BADGING_FORCED_TRUE = "force_badging_true_for_bug";
169
170 // STOPSHIP (b/142218092) this should be removed before ship
171 static boolean wasBadgingForcedTrue(Context context) {
172 return Settings.Secure.getInt(context.getContentResolver(), BADGING_FORCED_TRUE, 0) != 0;
173 }
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,
177 AppOpsManager appOpsManager) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400178 mContext = context;
179 mZenModeHelper = zenHelper;
180 mRankingHandler = rankingHandler;
181 mPm = pm;
Will Brockman23db6d42020-02-28 09:51:12 -0500182 mNotificationChannelLogger = notificationChannelLogger;
Mady Mellorc888d512020-04-09 14:48:02 -0700183 mAppOps = appOpsManager;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400184
Mady Mellor973bc212019-11-01 10:06:49 -0700185 // STOPSHIP (b/142218092) this should be removed before ship
186 if (!wasBadgingForcedTrue(context)) {
187 Settings.Secure.putInt(mContext.getContentResolver(),
188 Settings.Secure.NOTIFICATION_BADGING,
189 DEFAULT_SHOW_BADGE ? 1 : 0);
190 Settings.Secure.putInt(context.getContentResolver(), BADGING_FORCED_TRUE, 1);
191 }
192
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400193 updateBadgingEnabled();
Julia Reynolds4509ce72019-01-31 13:12:43 -0500194 updateBubblesEnabled();
Beverly0479cde22018-11-09 11:05:34 -0500195 syncChannelsBypassingDnd(mContext.getUserId());
Julia Reynoldse24faa22020-04-02 12:44:47 -0400196 mAllowInvalidShortcuts = Settings.Global.getInt(mContext.getContentResolver(),
197 Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 0;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400198 }
199
Annie Meng8b646fd2019-02-01 18:46:42 +0000200 public void readXml(XmlPullParser parser, boolean forRestore, int userId)
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400201 throws XmlPullParserException, IOException {
202 int type = parser.getEventType();
203 if (type != XmlPullParser.START_TAG) return;
204 String tag = parser.getName();
205 if (!TAG_RANKING.equals(tag)) return;
Mady Mellorc888d512020-04-09 14:48:02 -0700206
207 boolean upgradeForBubbles = false;
208 if (parser.getAttributeCount() > 0) {
209 String attribute = parser.getAttributeName(0);
210 if (ATT_VERSION.equals(attribute)) {
211 int xmlVersion = Integer.parseInt(parser.getAttributeValue(0));
212 upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
213 }
214 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400215 synchronized (mPackagePreferences) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400216 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
217 tag = parser.getName();
218 if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
219 return;
220 }
221 if (type == XmlPullParser.START_TAG) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500222 if (TAG_STATUS_ICONS.equals(tag)) {
Annie Meng8b646fd2019-02-01 18:46:42 +0000223 if (forRestore && userId != UserHandle.USER_SYSTEM) {
224 continue;
225 }
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500226 mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute(
227 parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
228 } else if (TAG_PACKAGE.equals(tag)) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400229 int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
230 String name = parser.getAttributeValue(null, ATT_NAME);
231 if (!TextUtils.isEmpty(name)) {
232 if (forRestore) {
233 try {
Annie Meng8b646fd2019-02-01 18:46:42 +0000234 uid = mPm.getPackageUidAsUser(name, userId);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400235 } catch (PackageManager.NameNotFoundException e) {
236 // noop
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400237 }
238 }
Julia Reynoldsc29370a2019-08-20 16:08:42 -0400239 boolean skipWarningLogged = false;
Mady Mellorc888d512020-04-09 14:48:02 -0700240 boolean hasSAWPermission = false;
241 if (upgradeForBubbles) {
242 hasSAWPermission = mAppOps.noteOpNoThrow(
243 OP_SYSTEM_ALERT_WINDOW, uid, name, null,
244 "check-notif-bubble") == AppOpsManager.MODE_ALLOWED;
245 }
246 int bubblePref = hasSAWPermission
247 ? BUBBLE_PREFERENCE_ALL
248 : XmlUtils.readIntAttribute(parser, ATT_ALLOW_BUBBLE,
249 DEFAULT_BUBBLE_PREFERENCE);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400250
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500251 PackagePreferences r = getOrCreatePackagePreferencesLocked(
252 name, userId, uid,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400253 XmlUtils.readIntAttribute(
254 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
255 XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
256 DEFAULT_PRIORITY),
257 XmlUtils.readIntAttribute(
258 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
259 XmlUtils.readBooleanAttribute(
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500260 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
Mady Mellorc888d512020-04-09 14:48:02 -0700261 bubblePref);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400262 r.importance = XmlUtils.readIntAttribute(
263 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
264 r.priority = XmlUtils.readIntAttribute(
265 parser, ATT_PRIORITY, DEFAULT_PRIORITY);
266 r.visibility = XmlUtils.readIntAttribute(
267 parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
268 r.showBadge = XmlUtils.readBooleanAttribute(
269 parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
270 r.lockedAppFields = XmlUtils.readIntAttribute(parser,
271 ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
272
273 final int innerDepth = parser.getDepth();
274 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
275 && (type != XmlPullParser.END_TAG
276 || parser.getDepth() > innerDepth)) {
277 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
278 continue;
279 }
280
281 String tagName = parser.getName();
282 // Channel groups
283 if (TAG_GROUP.equals(tagName)) {
284 String id = parser.getAttributeValue(null, ATT_ID);
285 CharSequence groupName = parser.getAttributeValue(null,
286 ATT_NAME);
287 if (!TextUtils.isEmpty(id)) {
288 NotificationChannelGroup group
289 = new NotificationChannelGroup(id, groupName);
290 group.populateFromXml(parser);
291 r.groups.put(id, group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400292 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400293 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400294 // Channels
295 if (TAG_CHANNEL.equals(tagName)) {
Julia Reynoldsc29370a2019-08-20 16:08:42 -0400296 if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
297 if (!skipWarningLogged) {
298 Slog.w(TAG, "Skipping further channels for " + r.pkg
299 + "; app has too many");
300 skipWarningLogged = true;
301 }
302 continue;
303 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400304 String id = parser.getAttributeValue(null, ATT_ID);
305 String channelName = parser.getAttributeValue(null, ATT_NAME);
306 int channelImportance = XmlUtils.readIntAttribute(
307 parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
308 if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
309 NotificationChannel channel = new NotificationChannel(id,
310 channelName, channelImportance);
311 if (forRestore) {
312 channel.populateFromXmlForRestore(parser, mContext);
313 } else {
314 channel.populateFromXml(parser);
315 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -0400316 channel.setImportanceLockedByCriticalDeviceFunction(
317 r.defaultAppLockedImportance);
Julia Reynolds7c267522020-01-16 11:26:41 -0500318 boolean isInvalidShortcutChannel =
319 channel.getConversationId() != null &&
320 channel.getConversationId().contains(
321 PLACEHOLDER_CONVERSATION_ID);
322 if (mAllowInvalidShortcuts || (!mAllowInvalidShortcuts
323 && !isInvalidShortcutChannel)) {
324 r.channels.put(id, channel);
325 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400326 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400327 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400328 // Delegate
329 if (TAG_DELEGATE.equals(tagName)) {
330 int delegateId =
331 XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
332 String delegateName =
333 XmlUtils.readStringAttribute(parser, ATT_NAME);
334 boolean delegateEnabled = XmlUtils.readBooleanAttribute(
335 parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
336 boolean userAllowed = XmlUtils.readBooleanAttribute(
337 parser, ATT_USER_ALLOWED,
338 Delegate.DEFAULT_USER_ALLOWED);
339 Delegate d = null;
340 if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(
341 delegateName)) {
342 d = new Delegate(
343 delegateName, delegateId, delegateEnabled,
344 userAllowed);
345 }
346 r.delegate = d;
347 }
348
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400349 }
350
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400351 try {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400352 deleteDefaultChannelIfNeededLocked(r);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400353 } catch (PackageManager.NameNotFoundException e) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400354 Slog.e(TAG, "deleteDefaultChannelIfNeededLocked - Exception: " + e);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400355 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400356 }
357 }
358 }
359 }
360 }
361 throw new IllegalStateException("Failed to reach END_DOCUMENT");
362 }
363
Julia Reynolds5c399c62019-04-08 14:42:53 -0400364 private PackagePreferences getPackagePreferencesLocked(String pkg, int uid) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400365 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400366 return mPackagePreferences.get(key);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400367 }
368
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500369 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
370 int uid) {
371 return getOrCreatePackagePreferencesLocked(pkg, UserHandle.getUserId(uid), uid,
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500372 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
Mady Mellora92268c2020-03-09 17:25:08 -0700373 DEFAULT_BUBBLE_PREFERENCE);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400374 }
375
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500376 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
377 @UserIdInt int userId, int uid) {
378 return getOrCreatePackagePreferencesLocked(pkg, userId, uid,
379 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
Mady Mellora92268c2020-03-09 17:25:08 -0700380 DEFAULT_BUBBLE_PREFERENCE);
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500381 }
382
383 private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
384 @UserIdInt int userId, int uid, int importance, int priority, int visibility,
Mady Mellora92268c2020-03-09 17:25:08 -0700385 boolean showBadge, int bubblePreference) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400386 final String key = packagePreferencesKey(pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400387 PackagePreferences
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500388 r = (uid == UNKNOWN_UID)
389 ? mRestoredWithoutUids.get(unrestoredPackageKey(pkg, userId))
Julia Reynolds5c399c62019-04-08 14:42:53 -0400390 : mPackagePreferences.get(key);
391 if (r == null) {
392 r = new PackagePreferences();
393 r.pkg = pkg;
394 r.uid = uid;
395 r.importance = importance;
396 r.priority = priority;
397 r.visibility = visibility;
398 r.showBadge = showBadge;
Mady Mellora92268c2020-03-09 17:25:08 -0700399 r.bubblePreference = bubblePreference;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400400
Julia Reynolds5c399c62019-04-08 14:42:53 -0400401 try {
402 createDefaultChannelIfNeededLocked(r);
403 } catch (PackageManager.NameNotFoundException e) {
404 Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400405 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400406
407 if (r.uid == UNKNOWN_UID) {
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500408 mRestoredWithoutUids.put(unrestoredPackageKey(pkg, userId), r);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400409 } else {
410 mPackagePreferences.put(key, r);
411 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400412 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400413 return r;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400414 }
415
416 private boolean shouldHaveDefaultChannel(PackagePreferences r) throws
417 PackageManager.NameNotFoundException {
418 final int userId = UserHandle.getUserId(r.uid);
419 final ApplicationInfo applicationInfo =
420 mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
421 if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
422 // O apps should not have the default channel.
423 return false;
424 }
425
426 // Otherwise, this app should have the default channel.
427 return true;
428 }
429
Julia Reynolds996c7c12019-05-24 10:25:33 -0400430 private boolean deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400431 PackageManager.NameNotFoundException {
432 if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
433 // Not present
Julia Reynolds996c7c12019-05-24 10:25:33 -0400434 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400435 }
436
437 if (shouldHaveDefaultChannel(r)) {
438 // Keep the default channel until upgraded.
Julia Reynolds996c7c12019-05-24 10:25:33 -0400439 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400440 }
441
442 // Remove Default Channel.
443 r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
Julia Reynolds996c7c12019-05-24 10:25:33 -0400444
445 return true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400446 }
447
Julia Reynolds996c7c12019-05-24 10:25:33 -0400448 private boolean createDefaultChannelIfNeededLocked(PackagePreferences r) throws
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400449 PackageManager.NameNotFoundException {
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -0500450 if (r.uid == UNKNOWN_UID) {
451 return false;
452 }
453
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400454 if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
455 r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
456 com.android.internal.R.string.default_notification_channel_label));
Julia Reynolds996c7c12019-05-24 10:25:33 -0400457 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400458 }
459
460 if (!shouldHaveDefaultChannel(r)) {
461 // Keep the default channel until upgraded.
Julia Reynolds996c7c12019-05-24 10:25:33 -0400462 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400463 }
464
465 // Create Default Channel
466 NotificationChannel channel;
467 channel = new NotificationChannel(
468 NotificationChannel.DEFAULT_CHANNEL_ID,
469 mContext.getString(R.string.default_notification_channel_label),
470 r.importance);
471 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
472 channel.setLockscreenVisibility(r.visibility);
473 if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
474 channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
475 }
476 if (r.priority != DEFAULT_PRIORITY) {
477 channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
478 }
479 if (r.visibility != DEFAULT_VISIBILITY) {
480 channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
481 }
482 r.channels.put(channel.getId(), channel);
Julia Reynolds996c7c12019-05-24 10:25:33 -0400483
484 return true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400485 }
486
Annie Meng8b646fd2019-02-01 18:46:42 +0000487 public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400488 out.startTag(null, TAG_RANKING);
489 out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
Annie Meng8b646fd2019-02-01 18:46:42 +0000490 if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS
491 && (!forBackup || userId == UserHandle.USER_SYSTEM)) {
Julia Reynolds12ad7ca2019-01-28 09:29:16 -0500492 out.startTag(null, TAG_STATUS_ICONS);
493 out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons));
494 out.endTag(null, TAG_STATUS_ICONS);
495 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400496
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400497 synchronized (mPackagePreferences) {
498 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400499 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -0400500 final PackagePreferences r = mPackagePreferences.valueAt(i);
Annie Meng8b646fd2019-02-01 18:46:42 +0000501 if (forBackup && UserHandle.getUserId(r.uid) != userId) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400502 continue;
503 }
504 final boolean hasNonDefaultSettings =
505 r.importance != DEFAULT_IMPORTANCE
506 || r.priority != DEFAULT_PRIORITY
507 || r.visibility != DEFAULT_VISIBILITY
508 || r.showBadge != DEFAULT_SHOW_BADGE
509 || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
510 || r.channels.size() > 0
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400511 || r.groups.size() > 0
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500512 || r.delegate != null
Mady Mellora92268c2020-03-09 17:25:08 -0700513 || r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400514 if (hasNonDefaultSettings) {
515 out.startTag(null, TAG_PACKAGE);
516 out.attribute(null, ATT_NAME, r.pkg);
517 if (r.importance != DEFAULT_IMPORTANCE) {
518 out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
519 }
520 if (r.priority != DEFAULT_PRIORITY) {
521 out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
522 }
523 if (r.visibility != DEFAULT_VISIBILITY) {
524 out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
525 }
Mady Mellora92268c2020-03-09 17:25:08 -0700526 if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) {
527 out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(r.bubblePreference));
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500528 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400529 out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
530 out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
531 Integer.toString(r.lockedAppFields));
532
533 if (!forBackup) {
534 out.attribute(null, ATT_UID, Integer.toString(r.uid));
535 }
536
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -0400537 if (r.delegate != null) {
538 out.startTag(null, TAG_DELEGATE);
539
540 out.attribute(null, ATT_NAME, r.delegate.mPkg);
541 out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
542 if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
543 out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
544 }
545 if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
546 out.attribute(null, ATT_USER_ALLOWED,
547 Boolean.toString(r.delegate.mUserAllowed));
548 }
549 out.endTag(null, TAG_DELEGATE);
550 }
551
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400552 for (NotificationChannelGroup group : r.groups.values()) {
553 group.writeXml(out);
554 }
555
556 for (NotificationChannel channel : r.channels.values()) {
557 if (forBackup) {
558 if (!channel.isDeleted()) {
559 channel.writeXmlForBackup(out, mContext);
560 }
561 } else {
562 channel.writeXml(out);
563 }
564 }
565
566 out.endTag(null, TAG_PACKAGE);
567 }
568 }
569 }
570 out.endTag(null, TAG_RANKING);
571 }
572
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800573 /**
574 * Sets whether bubbles are allowed.
575 *
576 * @param pkg the package to allow or not allow bubbles for.
577 * @param uid the uid to allow or not allow bubbles for.
Mady Mellora92268c2020-03-09 17:25:08 -0700578 * @param bubblePreference whether bubbles are allowed.
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800579 */
Mady Mellora92268c2020-03-09 17:25:08 -0700580 public void setBubblesAllowed(String pkg, int uid, int bubblePreference) {
Mady Mellor9f296142019-05-24 09:42:52 -0700581 boolean changed = false;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400582 synchronized (mPackagePreferences) {
583 PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid);
Mady Mellora92268c2020-03-09 17:25:08 -0700584 changed = p.bubblePreference != bubblePreference;
585 p.bubblePreference = bubblePreference;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400586 p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
587 }
Mady Mellor9f296142019-05-24 09:42:52 -0700588 if (changed) {
589 updateConfig();
590 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500591 }
592
Mady Mellorc39b4ae2019-01-09 17:11:37 -0800593 /**
594 * Whether bubbles are allowed.
595 *
596 * @param pkg the package to check if bubbles are allowed for
597 * @param uid the uid to check if bubbles are allowed for.
598 * @return whether bubbles are allowed.
599 */
Julia Reynolds4509ce72019-01-31 13:12:43 -0500600 @Override
Mady Mellora92268c2020-03-09 17:25:08 -0700601 public int getBubblePreference(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400602 synchronized (mPackagePreferences) {
Mady Mellora92268c2020-03-09 17:25:08 -0700603 return getOrCreatePackagePreferencesLocked(pkg, uid).bubblePreference;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400604 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500605 }
606
607 public int getAppLockedFields(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400608 synchronized (mPackagePreferences) {
609 return getOrCreatePackagePreferencesLocked(pkg, uid).lockedAppFields;
610 }
Julia Reynolds33ab8a02018-12-17 16:19:52 -0500611 }
612
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400613 /**
614 * Gets importance.
615 */
616 @Override
617 public int getImportance(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400618 synchronized (mPackagePreferences) {
619 return getOrCreatePackagePreferencesLocked(packageName, uid).importance;
620 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400621 }
622
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400623 /**
624 * Returns whether the importance of the corresponding notification is user-locked and shouldn't
625 * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
626 * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
627 */
628 public boolean getIsAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400629 synchronized (mPackagePreferences) {
630 int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields;
631 return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
632 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400633 }
634
635 @Override
636 public boolean canShowBadge(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400637 synchronized (mPackagePreferences) {
638 return getOrCreatePackagePreferencesLocked(packageName, uid).showBadge;
639 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400640 }
641
642 @Override
643 public void setShowBadge(String packageName, int uid, boolean showBadge) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400644 synchronized (mPackagePreferences) {
645 getOrCreatePackagePreferencesLocked(packageName, uid).showBadge = showBadge;
646 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400647 updateConfig();
648 }
649
650 @Override
651 public boolean isGroupBlocked(String packageName, int uid, String groupId) {
652 if (groupId == null) {
653 return false;
654 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400655 synchronized (mPackagePreferences) {
656 PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
657 NotificationChannelGroup group = r.groups.get(groupId);
658 if (group == null) {
659 return false;
660 }
661 return group.isBlocked();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400662 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400663 }
664
665 int getPackagePriority(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400666 synchronized (mPackagePreferences) {
667 return getOrCreatePackagePreferencesLocked(pkg, uid).priority;
668 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400669 }
670
671 int getPackageVisibility(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400672 synchronized (mPackagePreferences) {
673 return getOrCreatePackagePreferencesLocked(pkg, uid).visibility;
674 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400675 }
676
677 @Override
678 public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
679 boolean fromTargetApp) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +0000680 Objects.requireNonNull(pkg);
681 Objects.requireNonNull(group);
682 Objects.requireNonNull(group.getId());
683 Objects.requireNonNull(!TextUtils.isEmpty(group.getName()));
Julia Reynolds5c399c62019-04-08 14:42:53 -0400684 synchronized (mPackagePreferences) {
685 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
686 if (r == null) {
687 throw new IllegalArgumentException("Invalid package");
688 }
689 final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400690 if (oldGroup != null) {
691 group.setChannels(oldGroup.getChannels());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400692
Julia Reynolds5c399c62019-04-08 14:42:53 -0400693 // apps can't update the blocked status or app overlay permission
694 if (fromTargetApp) {
695 group.setBlocked(oldGroup.isBlocked());
696 group.unlockFields(group.getUserLockedFields());
697 group.lockFields(oldGroup.getUserLockedFields());
698 } else {
699 // but the system can
700 if (group.isBlocked() != oldGroup.isBlocked()) {
701 group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
702 updateChannelsBypassingDnd(mContext.getUserId());
703 }
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -0400704 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400705 }
Will Brockman23db6d42020-02-28 09:51:12 -0500706 if (!group.equals(oldGroup)) {
707 // will log for new entries as well as name/description changes
708 MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
709 mNotificationChannelLogger.logNotificationChannelGroup(group, uid, pkg,
710 oldGroup == null,
711 (oldGroup != null) && oldGroup.isBlocked());
712 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400713 r.groups.put(group.getId(), group);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400714 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400715 }
716
717 @Override
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400718 public boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400719 boolean fromTargetApp, boolean hasDndAccess) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +0000720 Objects.requireNonNull(pkg);
721 Objects.requireNonNull(channel);
722 Objects.requireNonNull(channel.getId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400723 Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
Will Brockman23db6d42020-02-28 09:51:12 -0500724 boolean needsPolicyFileChange = false, wasUndeleted = false;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400725 synchronized (mPackagePreferences) {
726 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
727 if (r == null) {
728 throw new IllegalArgumentException("Invalid package");
729 }
730 if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
731 throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
732 }
733 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
734 throw new IllegalArgumentException("Reserved id");
735 }
736 NotificationChannel existing = r.channels.get(channel.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400737 if (existing != null && fromTargetApp) {
Will Brockman23db6d42020-02-28 09:51:12 -0500738 // Actually modifying an existing channel - keep most of the existing settings
Julia Reynolds5c399c62019-04-08 14:42:53 -0400739 if (existing.isDeleted()) {
Will Brockman23db6d42020-02-28 09:51:12 -0500740 // The existing channel was deleted - undelete it.
Julia Reynolds5c399c62019-04-08 14:42:53 -0400741 existing.setDeleted(false);
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400742 needsPolicyFileChange = true;
Will Brockman23db6d42020-02-28 09:51:12 -0500743 wasUndeleted = true;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400744
Julia Reynolds5c399c62019-04-08 14:42:53 -0400745 // log a resurrected channel as if it's new again
746 MetricsLogger.action(getChannelLog(channel, pkg).setType(
747 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
Will Brockman23db6d42020-02-28 09:51:12 -0500748 mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400749 }
750
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400751 if (!Objects.equals(channel.getName().toString(), existing.getName().toString())) {
752 existing.setName(channel.getName().toString());
753 needsPolicyFileChange = true;
754 }
755 if (!Objects.equals(channel.getDescription(), existing.getDescription())) {
756 existing.setDescription(channel.getDescription());
757 needsPolicyFileChange = true;
758 }
Hall Liu9866aa82020-03-12 12:55:50 -0700759 if (channel.isBlockable() != existing.isBlockable()) {
760 existing.setBlockable(channel.isBlockable());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400761 needsPolicyFileChange = true;
762 }
763 if (channel.getGroup() != null && existing.getGroup() == null) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400764 existing.setGroup(channel.getGroup());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400765 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400766 }
767
768 // Apps are allowed to downgrade channel importance if the user has not changed any
769 // fields on this channel yet.
770 final int previousExistingImportance = existing.getImportance();
771 if (existing.getUserLockedFields() == 0 &&
772 channel.getImportance() < existing.getImportance()) {
773 existing.setImportance(channel.getImportance());
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400774 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400775 }
776
777 // system apps and dnd access apps can bypass dnd if the user hasn't changed any
778 // fields on the channel yet
779 if (existing.getUserLockedFields() == 0 && hasDndAccess) {
780 boolean bypassDnd = channel.canBypassDnd();
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400781 if (bypassDnd != existing.canBypassDnd()) {
782 existing.setBypassDnd(bypassDnd);
783 needsPolicyFileChange = true;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400784
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400785 if (bypassDnd != mAreChannelsBypassingDnd
786 || previousExistingImportance != existing.getImportance()) {
787 updateChannelsBypassingDnd(mContext.getUserId());
788 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400789 }
790 }
791
Julia Reynolds469144c2019-06-21 14:30:28 -0400792 if (existing.getOriginalImportance() == IMPORTANCE_UNSPECIFIED) {
793 existing.setOriginalImportance(channel.getImportance());
794 needsPolicyFileChange = true;
795 }
796
Julia Reynolds5c399c62019-04-08 14:42:53 -0400797 updateConfig();
Will Brockman23db6d42020-02-28 09:51:12 -0500798 if (needsPolicyFileChange && !wasUndeleted) {
799 mNotificationChannelLogger.logNotificationChannelModified(existing, uid, pkg,
800 previousExistingImportance, false);
801 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400802 return needsPolicyFileChange;
Julia Reynolds5c399c62019-04-08 14:42:53 -0400803 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400804
Julia Reynoldsc29370a2019-08-20 16:08:42 -0400805 if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
806 throw new IllegalStateException("Limit exceed; cannot create more channels");
807 }
808
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400809 needsPolicyFileChange = true;
810
Julia Reynolds5c399c62019-04-08 14:42:53 -0400811 if (channel.getImportance() < IMPORTANCE_NONE
812 || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
813 throw new IllegalArgumentException("Invalid importance level");
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400814 }
815
Julia Reynolds5c399c62019-04-08 14:42:53 -0400816 // Reset fields that apps aren't allowed to set.
817 if (fromTargetApp && !hasDndAccess) {
818 channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400819 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400820 if (fromTargetApp) {
821 channel.setLockscreenVisibility(r.visibility);
Mady Mellora92268c2020-03-09 17:25:08 -0700822 channel.setAllowBubbles(existing != null && existing.canBubble());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400823 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400824 clearLockedFieldsLocked(channel);
825 channel.setImportanceLockedByOEM(r.oemLockedImportance);
826 if (!channel.isImportanceLockedByOEM()) {
Julia Reynolds72b28442019-11-12 11:43:39 -0500827 if (r.oemLockedChannels.contains(channel.getId())) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400828 channel.setImportanceLockedByOEM(true);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400829 }
830 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400831 channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
832 if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
833 channel.setLockscreenVisibility(
834 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
Julia Reynolds413ba842019-01-11 10:38:08 -0500835 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400836 if (!r.showBadge) {
837 channel.setShowBadge(false);
838 }
Julia Reynolds469144c2019-06-21 14:30:28 -0400839 channel.setOriginalImportance(channel.getImportance());
Julia Reynolds0f767342019-12-18 09:11:55 -0500840
841 // validate parent
842 if (channel.getParentChannelId() != null) {
843 Preconditions.checkArgument(r.channels.containsKey(channel.getParentChannelId()),
844 "Tried to create a conversation channel without a preexisting parent");
845 }
846
Julia Reynolds5c399c62019-04-08 14:42:53 -0400847 r.channels.put(channel.getId(), channel);
848 if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
849 updateChannelsBypassingDnd(mContext.getUserId());
850 }
851 MetricsLogger.action(getChannelLog(channel, pkg).setType(
852 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
Will Brockman23db6d42020-02-28 09:51:12 -0500853 mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400854 }
Julia Reynoldsdafd3a42019-05-24 13:33:28 -0400855
856 return needsPolicyFileChange;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400857 }
858
Julia Reynolds5c399c62019-04-08 14:42:53 -0400859 void clearLockedFieldsLocked(NotificationChannel channel) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400860 channel.unlockFields(channel.getUserLockedFields());
861 }
862
863 @Override
864 public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
865 boolean fromUser) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +0000866 Objects.requireNonNull(updatedChannel);
867 Objects.requireNonNull(updatedChannel.getId());
Julia Reynolds5c399c62019-04-08 14:42:53 -0400868 synchronized (mPackagePreferences) {
869 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
870 if (r == null) {
871 throw new IllegalArgumentException("Invalid package");
872 }
873 NotificationChannel channel = r.channels.get(updatedChannel.getId());
874 if (channel == null || channel.isDeleted()) {
875 throw new IllegalArgumentException("Channel does not exist");
876 }
877 if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
878 updatedChannel.setLockscreenVisibility(
879 NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
880 }
881 if (fromUser) {
882 updatedChannel.lockFields(channel.getUserLockedFields());
883 lockFieldsForUpdateLocked(channel, updatedChannel);
884 } else {
885 updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
886 }
887 // no importance updates are allowed if OEM blocked it
888 updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
889 if (updatedChannel.isImportanceLockedByOEM()) {
890 updatedChannel.setImportance(channel.getImportance());
891 }
892 updatedChannel.setImportanceLockedByCriticalDeviceFunction(
893 r.defaultAppLockedImportance);
Beverly47679222019-05-16 15:46:11 -0400894 if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
895 && updatedChannel.getImportance() == IMPORTANCE_NONE) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400896 updatedChannel.setImportance(channel.getImportance());
897 }
Julia Reynolds413ba842019-01-11 10:38:08 -0500898
Julia Reynolds5c399c62019-04-08 14:42:53 -0400899 r.channels.put(updatedChannel.getId(), updatedChannel);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400900
Julia Reynolds5c399c62019-04-08 14:42:53 -0400901 if (onlyHasDefaultChannel(pkg, uid)) {
902 // copy settings to app level so they are inherited by new channels
903 // when the app migrates
904 r.importance = updatedChannel.getImportance();
905 r.priority = updatedChannel.canBypassDnd()
906 ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
907 r.visibility = updatedChannel.getLockscreenVisibility();
908 r.showBadge = updatedChannel.canShowBadge();
909 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400910
Julia Reynolds5c399c62019-04-08 14:42:53 -0400911 if (!channel.equals(updatedChannel)) {
912 // only log if there are real changes
913 MetricsLogger.action(getChannelLog(updatedChannel, pkg)
914 .setSubtype(fromUser ? 1 : 0));
Will Brockman23db6d42020-02-28 09:51:12 -0500915 mNotificationChannelLogger.logNotificationChannelModified(updatedChannel, uid, pkg,
916 channel.getImportance(), fromUser);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400917 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400918
Julia Reynolds5c399c62019-04-08 14:42:53 -0400919 if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
920 || channel.getImportance() != updatedChannel.getImportance()) {
921 updateChannelsBypassingDnd(mContext.getUserId());
922 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400923 }
924 updateConfig();
925 }
926
927 @Override
928 public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
929 boolean includeDeleted) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +0000930 Objects.requireNonNull(pkg);
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500931 return getConversationNotificationChannel(pkg, uid, channelId, null, true, includeDeleted);
Julia Reynolds0f767342019-12-18 09:11:55 -0500932 }
933
934 @Override
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500935 public NotificationChannel getConversationNotificationChannel(String pkg, int uid,
936 String channelId, String conversationId, boolean returnParentIfNoConversationChannel,
937 boolean includeDeleted) {
Julia Reynolds0f767342019-12-18 09:11:55 -0500938 Preconditions.checkNotNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -0400939 synchronized (mPackagePreferences) {
940 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
941 if (r == null) {
942 return null;
943 }
944 if (channelId == null) {
945 channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
946 }
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500947 NotificationChannel channel = null;
948 if (conversationId != null) {
949 // look for an automatically created conversation specific channel
950 channel = findConversationChannel(r, channelId, conversationId, includeDeleted);
951 }
952 if (channel == null && returnParentIfNoConversationChannel) {
953 // look for it just based on its id
Julia Reynolds0f767342019-12-18 09:11:55 -0500954 final NotificationChannel nc = r.channels.get(channelId);
955 if (nc != null && (includeDeleted || !nc.isDeleted())) {
956 return nc;
957 }
Julia Reynolds5c399c62019-04-08 14:42:53 -0400958 }
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500959 return channel;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400960 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400961 }
962
Julia Reynolds0f767342019-12-18 09:11:55 -0500963 private NotificationChannel findConversationChannel(PackagePreferences p, String parentId,
964 String conversationId, boolean includeDeleted) {
965 for (NotificationChannel nc : p.channels.values()) {
966 if (conversationId.equals(nc.getConversationId())
967 && parentId.equals(nc.getParentChannelId())
968 && (includeDeleted || !nc.isDeleted())) {
969 return nc;
970 }
971 }
972 return null;
973 }
974
975 public List<NotificationChannel> getNotificationChannelsByConversationId(String pkg, int uid,
976 String conversationId) {
977 Preconditions.checkNotNull(pkg);
978 Preconditions.checkNotNull(conversationId);
979 List<NotificationChannel> channels = new ArrayList<>();
980 synchronized (mPackagePreferences) {
981 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
982 if (r == null) {
983 return channels;
984 }
985 for (NotificationChannel nc : r.channels.values()) {
986 if (conversationId.equals(nc.getConversationId())
987 && !nc.isDeleted()) {
988 channels.add(nc);
989 }
990 }
991 return channels;
992 }
993 }
994
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400995 @Override
996 public void deleteNotificationChannel(String pkg, int uid, String channelId) {
Julia Reynolds5c399c62019-04-08 14:42:53 -0400997 synchronized (mPackagePreferences) {
998 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
999 if (r == null) {
1000 return;
1001 }
1002 NotificationChannel channel = r.channels.get(channelId);
1003 if (channel != null) {
Will Brockman23db6d42020-02-28 09:51:12 -05001004 deleteNotificationChannelLocked(channel, pkg, uid);
1005 }
1006 }
1007 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001008
Will Brockman23db6d42020-02-28 09:51:12 -05001009 private void deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
1010 if (!channel.isDeleted()) {
1011 channel.setDeleted(true);
1012 LogMaker lm = getChannelLog(channel, pkg);
1013 lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
1014 MetricsLogger.action(lm);
1015 mNotificationChannelLogger.logNotificationChannelDeleted(channel, uid, pkg);
1016
1017 if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
1018 updateChannelsBypassingDnd(mContext.getUserId());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001019 }
1020 }
1021 }
1022
1023 @Override
1024 @VisibleForTesting
1025 public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001026 Objects.requireNonNull(pkg);
1027 Objects.requireNonNull(channelId);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001028 synchronized (mPackagePreferences) {
1029 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1030 if (r == null) {
1031 return;
1032 }
1033 r.channels.remove(channelId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001034 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001035 }
1036
1037 @Override
1038 public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001039 Objects.requireNonNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001040 synchronized (mPackagePreferences) {
1041 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1042 if (r == null) {
1043 return;
1044 }
1045 int N = r.channels.size() - 1;
1046 for (int i = N; i >= 0; i--) {
1047 String key = r.channels.keyAt(i);
1048 if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
1049 r.channels.remove(key);
1050 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001051 }
1052 }
1053 }
1054
Julia Reynolds12ad7ca2019-01-28 09:29:16 -05001055 public boolean shouldHideSilentStatusIcons() {
1056 return mHideSilentStatusBarIcons;
1057 }
1058
1059 public void setHideSilentStatusIcons(boolean hide) {
1060 mHideSilentStatusBarIcons = hide;
1061 }
1062
Julia Reynolds413ba842019-01-11 10:38:08 -05001063 public void lockChannelsForOEM(String[] appOrChannelList) {
1064 if (appOrChannelList == null) {
1065 return;
1066 }
1067 for (String appOrChannel : appOrChannelList) {
1068 if (!TextUtils.isEmpty(appOrChannel)) {
1069 String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
1070 if (appSplit != null && appSplit.length > 0) {
1071 String appName = appSplit[0];
1072 String channelId = appSplit.length == 2 ? appSplit[1] : null;
1073
1074 synchronized (mPackagePreferences) {
1075 for (PackagePreferences r : mPackagePreferences.values()) {
1076 if (r.pkg.equals(appName)) {
1077 if (channelId == null) {
1078 // lock all channels for the app
1079 r.oemLockedImportance = true;
1080 for (NotificationChannel channel : r.channels.values()) {
1081 channel.setImportanceLockedByOEM(true);
1082 }
1083 } else {
1084 NotificationChannel channel = r.channels.get(channelId);
1085 if (channel != null) {
1086 channel.setImportanceLockedByOEM(true);
Julia Reynolds413ba842019-01-11 10:38:08 -05001087 }
Julia Reynolds72b28442019-11-12 11:43:39 -05001088 // Also store the locked channels on the record, so they aren't
1089 // temporarily lost when data is cleared on the package
1090 r.oemLockedChannels.add(channelId);
Julia Reynolds413ba842019-01-11 10:38:08 -05001091 }
1092 }
1093 }
1094 }
1095 }
1096 }
1097 }
1098 }
1099
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001100 public void updateDefaultApps(int userId, ArraySet<String> toRemove,
1101 ArraySet<Pair<String, Integer>> toAdd) {
Julia Reynolds0c245002019-03-27 16:10:11 -04001102 synchronized (mPackagePreferences) {
1103 for (PackagePreferences p : mPackagePreferences.values()) {
1104 if (userId == UserHandle.getUserId(p.uid)) {
1105 if (toRemove != null && toRemove.contains(p.pkg)) {
1106 p.defaultAppLockedImportance = false;
1107 for (NotificationChannel channel : p.channels.values()) {
1108 channel.setImportanceLockedByCriticalDeviceFunction(false);
1109 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001110 }
1111 }
1112 }
1113 if (toAdd != null) {
1114 for (Pair<String, Integer> approvedApp : toAdd) {
1115 PackagePreferences p = getOrCreatePackagePreferencesLocked(approvedApp.first,
1116 approvedApp.second);
1117 p.defaultAppLockedImportance = true;
1118 for (NotificationChannel channel : p.channels.values()) {
1119 channel.setImportanceLockedByCriticalDeviceFunction(true);
Julia Reynolds0c245002019-03-27 16:10:11 -04001120 }
1121 }
1122 }
1123 }
1124 }
1125
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001126 public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
1127 int uid, String groupId, boolean includeDeleted) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001128 Objects.requireNonNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001129 synchronized (mPackagePreferences) {
1130 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1131 if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
1132 return null;
1133 }
1134 NotificationChannelGroup group = r.groups.get(groupId).clone();
1135 group.setChannels(new ArrayList<>());
1136 int N = r.channels.size();
1137 for (int i = 0; i < N; i++) {
1138 final NotificationChannel nc = r.channels.valueAt(i);
1139 if (includeDeleted || !nc.isDeleted()) {
1140 if (groupId.equals(nc.getGroup())) {
1141 group.addChannel(nc);
1142 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001143 }
1144 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001145 return group;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001146 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001147 }
1148
1149 public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
1150 int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001151 Objects.requireNonNull(pkg);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001152 synchronized (mPackagePreferences) {
1153 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1154 if (r == null) {
1155 return null;
1156 }
1157 return r.groups.get(groupId);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001158 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001159 }
1160
1161 @Override
1162 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001163 int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001164 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001165 Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001166 synchronized (mPackagePreferences) {
1167 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1168 if (r == null) {
1169 return ParceledListSlice.emptyList();
1170 }
1171 NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
1172 int N = r.channels.size();
1173 for (int i = 0; i < N; i++) {
1174 final NotificationChannel nc = r.channels.valueAt(i);
1175 if (includeDeleted || !nc.isDeleted()) {
1176 if (nc.getGroup() != null) {
1177 if (r.groups.get(nc.getGroup()) != null) {
1178 NotificationChannelGroup ncg = groups.get(nc.getGroup());
1179 if (ncg == null) {
1180 ncg = r.groups.get(nc.getGroup()).clone();
1181 ncg.setChannels(new ArrayList<>());
1182 groups.put(nc.getGroup(), ncg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001183
Julia Reynolds5c399c62019-04-08 14:42:53 -04001184 }
1185 ncg.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001186 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001187 } else {
1188 nonGrouped.addChannel(nc);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001189 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001190 }
1191 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001192 if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
1193 groups.put(null, nonGrouped);
1194 }
1195 if (includeEmpty) {
1196 for (NotificationChannelGroup group : r.groups.values()) {
1197 if (!groups.containsKey(group.getId())) {
1198 groups.put(group.getId(), group);
1199 }
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001200 }
1201 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001202 return new ParceledListSlice<>(new ArrayList<>(groups.values()));
Julia Reynolds13ed28b2018-09-21 15:20:13 -04001203 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001204 }
1205
1206 public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
1207 String groupId) {
1208 List<NotificationChannel> deletedChannels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001209 synchronized (mPackagePreferences) {
1210 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1211 if (r == null || TextUtils.isEmpty(groupId)) {
1212 return deletedChannels;
1213 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001214
Will Brockman23db6d42020-02-28 09:51:12 -05001215 NotificationChannelGroup channelGroup = r.groups.remove(groupId);
1216 if (channelGroup != null) {
1217 mNotificationChannelLogger.logNotificationChannelGroupDeleted(channelGroup, uid,
1218 pkg);
1219 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001220
Julia Reynolds5c399c62019-04-08 14:42:53 -04001221 int N = r.channels.size();
1222 for (int i = 0; i < N; i++) {
1223 final NotificationChannel nc = r.channels.valueAt(i);
1224 if (groupId.equals(nc.getGroup())) {
Will Brockman23db6d42020-02-28 09:51:12 -05001225 deleteNotificationChannelLocked(nc, pkg, uid);
Julia Reynolds5c399c62019-04-08 14:42:53 -04001226 deletedChannels.add(nc);
1227 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001228 }
1229 }
1230 return deletedChannels;
1231 }
1232
1233 @Override
1234 public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
1235 int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001236 List<NotificationChannelGroup> groups = new ArrayList<>();
1237 synchronized (mPackagePreferences) {
1238 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1239 if (r == null) {
1240 return groups;
1241 }
1242 groups.addAll(r.groups.values());
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001243 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001244 return groups;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001245 }
1246
Julia Reynolds02971452020-02-14 16:44:19 -05001247 public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
1248 synchronized (mPackagePreferences) {
1249 ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
1250
1251 for (PackagePreferences p : mPackagePreferences.values()) {
1252 int N = p.channels.size();
1253 for (int i = 0; i < N; i++) {
1254 final NotificationChannel nc = p.channels.valueAt(i);
1255 if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()
1256 && (nc.isImportantConversation() || !onlyImportant)) {
1257 ConversationChannelWrapper conversation = new ConversationChannelWrapper();
1258 conversation.setPkg(p.pkg);
1259 conversation.setUid(p.uid);
1260 conversation.setNotificationChannel(nc);
1261 conversation.setParentChannelLabel(
1262 p.channels.get(nc.getParentChannelId()).getName());
1263 boolean blockedByGroup = false;
1264 if (nc.getGroup() != null) {
1265 NotificationChannelGroup group = p.groups.get(nc.getGroup());
1266 if (group != null) {
1267 if (group.isBlocked()) {
1268 blockedByGroup = true;
1269 } else {
1270 conversation.setGroupLabel(group.getName());
1271 }
1272 }
1273 }
1274 if (!blockedByGroup) {
1275 conversations.add(conversation);
1276 }
1277 }
1278 }
1279 }
1280
1281 return conversations;
1282 }
1283 }
1284
Julia Reynolds882f2062020-02-05 12:11:38 -05001285 public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) {
1286 Objects.requireNonNull(pkg);
1287 synchronized (mPackagePreferences) {
1288 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1289 if (r == null) {
1290 return new ArrayList<>();
1291 }
1292 ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
1293 int N = r.channels.size();
1294 for (int i = 0; i < N; i++) {
1295 final NotificationChannel nc = r.channels.valueAt(i);
1296 if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()) {
1297 ConversationChannelWrapper conversation = new ConversationChannelWrapper();
Julia Reynolds02971452020-02-14 16:44:19 -05001298 conversation.setPkg(r.pkg);
1299 conversation.setUid(r.uid);
Julia Reynolds882f2062020-02-05 12:11:38 -05001300 conversation.setNotificationChannel(nc);
1301 conversation.setParentChannelLabel(
1302 r.channels.get(nc.getParentChannelId()).getName());
1303 boolean blockedByGroup = false;
1304 if (nc.getGroup() != null) {
1305 NotificationChannelGroup group = r.groups.get(nc.getGroup());
1306 if (group != null) {
1307 if (group.isBlocked()) {
1308 blockedByGroup = true;
1309 } else {
1310 conversation.setGroupLabel(group.getName());
1311 }
1312 }
1313 }
1314 if (!blockedByGroup) {
1315 conversations.add(conversation);
1316 }
1317 }
1318 }
1319
1320 return conversations;
1321 }
1322 }
1323
Julia Reynoldsa625b942020-02-15 09:42:23 -05001324 public @NonNull List<String> deleteConversation(String pkg, int uid, String conversationId) {
1325 synchronized (mPackagePreferences) {
1326 List<String> deletedChannelIds = new ArrayList<>();
1327 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1328 if (r == null) {
1329 return deletedChannelIds;
1330 }
1331 int N = r.channels.size();
1332 for (int i = 0; i < N; i++) {
1333 final NotificationChannel nc = r.channels.valueAt(i);
1334 if (conversationId.equals(nc.getConversationId())) {
1335 nc.setDeleted(true);
1336 LogMaker lm = getChannelLog(nc, pkg);
1337 lm.setType(
1338 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
1339 MetricsLogger.action(lm);
Will Brockman23db6d42020-02-28 09:51:12 -05001340 mNotificationChannelLogger.logNotificationChannelDeleted(nc, uid, pkg);
Julia Reynoldsa625b942020-02-15 09:42:23 -05001341
1342 deletedChannelIds.add(nc.getId());
1343 }
1344 }
1345 if (!deletedChannelIds.isEmpty() && mAreChannelsBypassingDnd) {
1346 updateChannelsBypassingDnd(mContext.getUserId());
1347 }
1348 return deletedChannelIds;
1349 }
1350 }
1351
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001352 @Override
1353 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
1354 boolean includeDeleted) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001355 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001356 List<NotificationChannel> channels = new ArrayList<>();
Julia Reynolds5c399c62019-04-08 14:42:53 -04001357 synchronized (mPackagePreferences) {
1358 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1359 if (r == null) {
1360 return ParceledListSlice.emptyList();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001361 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001362 int N = r.channels.size();
1363 for (int i = 0; i < N; i++) {
1364 final NotificationChannel nc = r.channels.valueAt(i);
1365 if (includeDeleted || !nc.isDeleted()) {
1366 channels.add(nc);
1367 }
1368 }
1369 return new ParceledListSlice<>(channels);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001370 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001371 }
1372
1373 /**
Beverly0479cde22018-11-09 11:05:34 -05001374 * Gets all notification channels associated with the given pkg and userId that can bypass dnd
1375 */
1376 public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
1377 int userId) {
1378 List<NotificationChannel> channels = new ArrayList<>();
1379 synchronized (mPackagePreferences) {
1380 final PackagePreferences r = mPackagePreferences.get(
1381 packagePreferencesKey(pkg, userId));
1382 // notifications from this package aren't blocked
1383 if (r != null && r.importance != IMPORTANCE_NONE) {
1384 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001385 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001386 channels.add(channel);
1387 }
1388 }
1389 }
1390 }
1391 return new ParceledListSlice<>(channels);
1392 }
1393
1394 /**
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001395 * True for pre-O apps that only have the default channel, or pre O apps that have no
1396 * channels yet. This method will create the default channel for pre-O apps that don't have it.
1397 * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
1398 * upgrades.
1399 */
1400 public boolean onlyHasDefaultChannel(String pkg, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001401 synchronized (mPackagePreferences) {
1402 PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
1403 if (r.channels.size() == 1
1404 && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
1405 return true;
1406 }
1407 return false;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001408 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001409 }
1410
1411 public int getDeletedChannelCount(String pkg, int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001412 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001413 int deletedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001414 synchronized (mPackagePreferences) {
1415 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1416 if (r == null) {
1417 return deletedCount;
1418 }
1419 int N = r.channels.size();
1420 for (int i = 0; i < N; i++) {
1421 final NotificationChannel nc = r.channels.valueAt(i);
1422 if (nc.isDeleted()) {
1423 deletedCount++;
1424 }
1425 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001426 return deletedCount;
1427 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001428 }
1429
1430 public int getBlockedChannelCount(String pkg, int uid) {
Daulet Zhanguzin9cb1a5e2020-01-02 17:32:47 +00001431 Objects.requireNonNull(pkg);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001432 int blockedCount = 0;
Julia Reynolds5c399c62019-04-08 14:42:53 -04001433 synchronized (mPackagePreferences) {
1434 PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
1435 if (r == null) {
1436 return blockedCount;
1437 }
1438 int N = r.channels.size();
1439 for (int i = 0; i < N; i++) {
1440 final NotificationChannel nc = r.channels.valueAt(i);
1441 if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
1442 blockedCount++;
1443 }
1444 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001445 return blockedCount;
1446 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001447 }
1448
1449 public int getBlockedAppCount(int userId) {
1450 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001451 synchronized (mPackagePreferences) {
1452 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001453 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001454 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001455 if (userId == UserHandle.getUserId(r.uid)
1456 && r.importance == IMPORTANCE_NONE) {
1457 count++;
1458 }
1459 }
1460 }
1461 return count;
1462 }
1463
Beverly0479cde22018-11-09 11:05:34 -05001464 /**
1465 * Returns the number of apps that have at least one notification channel that can bypass DND
1466 * for given particular user
1467 */
1468 public int getAppsBypassingDndCount(int userId) {
1469 int count = 0;
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001470 synchronized (mPackagePreferences) {
Beverly0479cde22018-11-09 11:05:34 -05001471 final int numPackagePreferences = mPackagePreferences.size();
1472 for (int i = 0; i < numPackagePreferences; i++) {
1473 final PackagePreferences r = mPackagePreferences.valueAt(i);
1474 // Package isn't associated with this userId or notifications from this package are
1475 // blocked
1476 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1477 continue;
1478 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001479
Beverly0479cde22018-11-09 11:05:34 -05001480 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001481 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Beverly0479cde22018-11-09 11:05:34 -05001482 count++;
1483 break;
1484 }
1485 }
1486 }
1487 }
1488 return count;
1489 }
1490
1491 /**
1492 * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
1493 * updating
1494 * @param userId
1495 */
1496 private void syncChannelsBypassingDnd(int userId) {
1497 mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
1498 & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
1499 updateChannelsBypassingDnd(userId);
1500 }
1501
1502 /**
1503 * Updates the user's NotificationPolicy based on whether the given userId
1504 * has channels bypassing DND
1505 * @param userId
1506 */
1507 private void updateChannelsBypassingDnd(int userId) {
1508 synchronized (mPackagePreferences) {
1509 final int numPackagePreferences = mPackagePreferences.size();
1510 for (int i = 0; i < numPackagePreferences; i++) {
1511 final PackagePreferences r = mPackagePreferences.valueAt(i);
1512 // Package isn't associated with this userId or notifications from this package are
1513 // blocked
1514 if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
1515 continue;
1516 }
1517
1518 for (NotificationChannel channel : r.channels.values()) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001519 if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001520 if (!mAreChannelsBypassingDnd) {
1521 mAreChannelsBypassingDnd = true;
1522 updateZenPolicy(true);
1523 }
1524 return;
1525 }
1526 }
1527 }
1528 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001529 // If no channels bypass DND, update the zen policy once to disable DND bypass.
1530 if (mAreChannelsBypassingDnd) {
1531 mAreChannelsBypassingDnd = false;
1532 updateZenPolicy(false);
1533 }
1534 }
1535
Julia Reynolds5c399c62019-04-08 14:42:53 -04001536 private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
Beverly0479cde22018-11-09 11:05:34 -05001537 // Channel is in a group that's blocked
Beverly4f7b53d2018-11-20 09:56:31 -05001538 if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
1539 return false;
Beverly0479cde22018-11-09 11:05:34 -05001540 }
1541
1542 // Channel is deleted or is blocked
1543 if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
1544 return false;
1545 }
1546
1547 return true;
1548 }
1549
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001550 public void updateZenPolicy(boolean areChannelsBypassingDnd) {
1551 NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
1552 mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
1553 policy.priorityCategories, policy.priorityCallSenders,
1554 policy.priorityMessageSenders, policy.suppressedVisualEffects,
1555 (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
Julia Reynolds24edc002020-01-29 16:35:32 -05001556 : 0),
1557 policy.priorityConversationSenders));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001558 }
1559
1560 public boolean areChannelsBypassingDnd() {
1561 return mAreChannelsBypassingDnd;
1562 }
1563
1564 /**
1565 * Sets importance.
1566 */
1567 @Override
1568 public void setImportance(String pkgName, int uid, int importance) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001569 synchronized (mPackagePreferences) {
1570 getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance;
1571 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001572 updateConfig();
1573 }
1574
1575 public void setEnabled(String packageName, int uid, boolean enabled) {
1576 boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
1577 if (wasEnabled == enabled) {
1578 return;
1579 }
1580 setImportance(packageName, uid,
1581 enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
1582 }
1583
1584 /**
1585 * Sets whether any notifications from the app, represented by the given {@code pkgName} and
1586 * {@code uid}, have their importance locked by the user. Locked notifications don't get
1587 * considered for sentiment adjustments (and thus never show a blocking helper).
1588 */
1589 public void setAppImportanceLocked(String packageName, int uid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001590 synchronized (mPackagePreferences) {
1591 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(packageName, uid);
1592 if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
1593 return;
1594 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001595
Julia Reynolds5c399c62019-04-08 14:42:53 -04001596 prefs.lockedAppFields =
1597 prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
1598 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001599 updateConfig();
1600 }
1601
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001602 /**
1603 * Returns the delegate for a given package, if it's allowed by the package and the user.
1604 */
1605 public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001606 synchronized (mPackagePreferences) {
1607 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001608
Julia Reynolds5c399c62019-04-08 14:42:53 -04001609 if (prefs == null || prefs.delegate == null) {
1610 return null;
1611 }
1612 if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
1613 return null;
1614 }
1615 return prefs.delegate.mPkg;
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001616 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001617 }
1618
1619 /**
1620 * Used by an app to delegate notification posting privileges to another apps.
1621 */
1622 public void setNotificationDelegate(String sourcePkg, int sourceUid,
1623 String delegatePkg, int delegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001624 synchronized (mPackagePreferences) {
1625 PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001626
Julia Reynolds5c399c62019-04-08 14:42:53 -04001627 boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
1628 Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
1629 prefs.delegate = delegate;
1630 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001631 updateConfig();
1632 }
1633
1634 /**
1635 * Used by an app to turn off its notification delegate.
1636 */
1637 public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001638 boolean changed = false;
1639 synchronized (mPackagePreferences) {
1640 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1641 if (prefs != null && prefs.delegate != null) {
1642 prefs.delegate.mEnabled = false;
1643 changed = true;
1644 }
1645 }
1646 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001647 updateConfig();
1648 }
1649 }
1650
1651 /**
1652 * Toggles whether an app can have a notification delegate on behalf of a user.
1653 */
1654 public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001655 boolean changed = false;
1656 synchronized (mPackagePreferences) {
1657 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
1658 if (prefs != null && prefs.delegate != null) {
1659 prefs.delegate.mUserAllowed = userAllowed;
1660 changed = true;
1661 }
1662 }
1663 if (changed) {
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001664 updateConfig();
1665 }
1666 }
1667
1668 /**
1669 * Returns whether the given app is allowed on post notifications on behalf of the other given
1670 * app.
1671 */
1672 public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
1673 String potentialDelegatePkg, int potentialDelegateUid) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001674 synchronized (mPackagePreferences) {
1675 PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001676
Julia Reynolds5c399c62019-04-08 14:42:53 -04001677 return prefs != null && prefs.isValidDelegate(potentialDelegatePkg,
1678 potentialDelegateUid);
1679 }
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001680 }
1681
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001682 @VisibleForTesting
Julia Reynolds5c399c62019-04-08 14:42:53 -04001683 void lockFieldsForUpdateLocked(NotificationChannel original, NotificationChannel update) {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001684 if (original.canBypassDnd() != update.canBypassDnd()) {
1685 update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
1686 }
1687 if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
1688 update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
1689 }
1690 if (original.getImportance() != update.getImportance()) {
1691 update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
1692 }
1693 if (original.shouldShowLights() != update.shouldShowLights()
1694 || original.getLightColor() != update.getLightColor()) {
1695 update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
1696 }
1697 if (!Objects.equals(original.getSound(), update.getSound())) {
1698 update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
1699 }
1700 if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
1701 || original.shouldVibrate() != update.shouldVibrate()) {
1702 update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
1703 }
1704 if (original.canShowBadge() != update.canShowBadge()) {
1705 update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
1706 }
Julia Reynolds4509ce72019-01-31 13:12:43 -05001707 if (original.canBubble() != update.canBubble()) {
Mady Mellorc39b4ae2019-01-09 17:11:37 -08001708 update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE);
Julia Reynoldsb6bd93d2018-10-24 09:22:38 -04001709 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001710 }
1711
1712 public void dump(PrintWriter pw, String prefix,
1713 @NonNull NotificationManagerService.DumpFilter filter) {
1714 pw.print(prefix);
1715 pw.println("per-package config:");
1716
Julia Reynolds5c399c62019-04-08 14:42:53 -04001717 pw.println("PackagePreferences:");
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001718 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001719 dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001720 }
1721 pw.println("Restored without uid:");
Julia Reynolds5c399c62019-04-08 14:42:53 -04001722 dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001723 }
1724
1725 public void dump(ProtoOutputStream proto,
1726 @NonNull NotificationManagerService.DumpFilter filter) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001727 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001728 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001729 mPackagePreferences);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001730 }
Julia Reynolds5c399c62019-04-08 14:42:53 -04001731 dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001732 mRestoredWithoutUids);
1733 }
1734
Julia Reynolds5c399c62019-04-08 14:42:53 -04001735 private static void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001736 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001737 ArrayMap<String, PackagePreferences> packagePreferences) {
1738 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001739 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001740 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001741 if (filter.matches(r.pkg)) {
1742 pw.print(prefix);
1743 pw.print(" AppSettings: ");
1744 pw.print(r.pkg);
1745 pw.print(" (");
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04001746 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001747 pw.print(')');
1748 if (r.importance != DEFAULT_IMPORTANCE) {
1749 pw.print(" importance=");
1750 pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
1751 }
1752 if (r.priority != DEFAULT_PRIORITY) {
1753 pw.print(" priority=");
1754 pw.print(Notification.priorityToString(r.priority));
1755 }
1756 if (r.visibility != DEFAULT_VISIBILITY) {
1757 pw.print(" visibility=");
1758 pw.print(Notification.visibilityToString(r.visibility));
1759 }
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001760 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1761 pw.print(" showBadge=");
1762 pw.print(r.showBadge);
1763 }
1764 if (r.defaultAppLockedImportance != DEFAULT_APP_LOCKED_IMPORTANCE) {
1765 pw.print(" defaultAppLocked=");
1766 pw.print(r.defaultAppLockedImportance);
1767 }
1768 if (r.oemLockedImportance != DEFAULT_OEM_LOCKED_IMPORTANCE) {
1769 pw.print(" oemLocked=");
1770 pw.print(r.oemLockedImportance);
1771 }
Julia Reynolds72b28442019-11-12 11:43:39 -05001772 if (!r.oemLockedChannels.isEmpty()) {
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001773 pw.print(" futureLockedChannels=");
Julia Reynolds72b28442019-11-12 11:43:39 -05001774 pw.print(r.oemLockedChannels);
Julia Reynoldse7ca31b2019-04-25 15:41:47 -04001775 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001776 pw.println();
1777 for (NotificationChannel channel : r.channels.values()) {
1778 pw.print(prefix);
1779 channel.dump(pw, " ", filter.redact);
1780 }
1781 for (NotificationChannelGroup group : r.groups.values()) {
1782 pw.print(prefix);
1783 pw.print(" ");
1784 pw.print(" ");
1785 pw.println(group);
1786 }
1787 }
1788 }
1789 }
1790
Julia Reynolds5c399c62019-04-08 14:42:53 -04001791 private static void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001792 @NonNull NotificationManagerService.DumpFilter filter,
Julia Reynolds5c399c62019-04-08 14:42:53 -04001793 ArrayMap<String, PackagePreferences> packagePreferences) {
1794 final int N = packagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001795 long fToken;
1796 for (int i = 0; i < N; i++) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04001797 final PackagePreferences r = packagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001798 if (filter.matches(r.pkg)) {
1799 fToken = proto.start(fieldId);
1800
1801 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
1802 proto.write(RankingHelperProto.RecordProto.UID, r.uid);
1803 proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
1804 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
1805 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
1806 proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
1807
1808 for (NotificationChannel channel : r.channels.values()) {
Jeffrey Huangcb782852019-12-05 11:28:11 -08001809 channel.dumpDebug(proto, RankingHelperProto.RecordProto.CHANNELS);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001810 }
1811 for (NotificationChannelGroup group : r.groups.values()) {
Jeffrey Huangcb782852019-12-05 11:28:11 -08001812 group.dumpDebug(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001813 }
1814
1815 proto.end(fToken);
1816 }
1817 }
1818 }
1819
Yotam Aron74299972020-01-16 16:20:58 +02001820 /**
1821 * Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}.
1822 */
1823 public void pullPackagePreferencesStats(List<StatsEvent> events) {
1824 synchronized (mPackagePreferences) {
1825 for (int i = 0; i < mPackagePreferences.size(); i++) {
1826 if (i > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
1827 break;
1828 }
1829 StatsEvent.Builder event = StatsEvent.newBuilder()
1830 .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
1831 final PackagePreferences r = mPackagePreferences.valueAt(i);
1832 event.writeInt(r.uid);
Muhammad Qureshi22e52da2020-03-30 21:14:21 -07001833 event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
Yotam Aron74299972020-01-16 16:20:58 +02001834 event.writeInt(r.importance);
1835 event.writeInt(r.visibility);
1836 event.writeInt(r.lockedAppFields);
1837 events.add(event.build());
1838 }
1839 }
1840 }
1841
1842 /**
1843 * Fills out {@link PackageNotificationChannelPreferences} proto and wraps it in a
1844 * {@link StatsEvent}.
1845 */
1846 public void pullPackageChannelPreferencesStats(List<StatsEvent> events) {
1847 synchronized (mPackagePreferences) {
1848 int totalChannelsPulled = 0;
1849 for (int i = 0; i < mPackagePreferences.size(); i++) {
1850 if (totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
1851 break;
1852 }
1853 final PackagePreferences r = mPackagePreferences.valueAt(i);
1854 for (NotificationChannel channel : r.channels.values()) {
1855 if (++totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
1856 break;
1857 }
1858 StatsEvent.Builder event = StatsEvent.newBuilder()
1859 .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
1860 event.writeInt(r.uid);
Muhammad Qureshi22e52da2020-03-30 21:14:21 -07001861 event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
Yotam Aron74299972020-01-16 16:20:58 +02001862 event.writeString(channel.getId());
1863 event.writeString(channel.getName().toString());
1864 event.writeString(channel.getDescription());
1865 event.writeInt(channel.getImportance());
1866 event.writeInt(channel.getUserLockedFields());
1867 event.writeBoolean(channel.isDeleted());
1868 events.add(event.build());
1869 }
1870 }
1871 }
1872 }
1873
1874 /**
1875 * Fills out {@link PackageNotificationChannelGroupPreferences} proto and wraps it in a
1876 * {@link StatsEvent}.
1877 */
1878 public void pullPackageChannelGroupPreferencesStats(List<StatsEvent> events) {
1879 synchronized (mPackagePreferences) {
1880 int totalGroupsPulled = 0;
1881 for (int i = 0; i < mPackagePreferences.size(); i++) {
1882 if (totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
1883 break;
1884 }
1885 final PackagePreferences r = mPackagePreferences.valueAt(i);
1886 for (NotificationChannelGroup groupChannel : r.groups.values()) {
1887 if (++totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
1888 break;
1889 }
1890 StatsEvent.Builder event = StatsEvent.newBuilder()
1891 .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
1892 event.writeInt(r.uid);
Muhammad Qureshi22e52da2020-03-30 21:14:21 -07001893 event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
Yotam Aron74299972020-01-16 16:20:58 +02001894 event.writeString(groupChannel.getId());
1895 event.writeString(groupChannel.getName().toString());
1896 event.writeString(groupChannel.getDescription());
1897 event.writeBoolean(groupChannel.isBlocked());
1898 event.writeInt(groupChannel.getUserLockedFields());
1899 events.add(event.build());
1900 }
1901 }
1902 }
1903 }
1904
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001905 public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
1906 JSONObject ranking = new JSONObject();
1907 JSONArray PackagePreferencess = new JSONArray();
1908 try {
1909 ranking.put("noUid", mRestoredWithoutUids.size());
1910 } catch (JSONException e) {
1911 // pass
1912 }
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001913 synchronized (mPackagePreferences) {
1914 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001915 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001916 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001917 if (filter == null || filter.matches(r.pkg)) {
1918 JSONObject PackagePreferences = new JSONObject();
1919 try {
1920 PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
1921 PackagePreferences.put("packageName", r.pkg);
1922 if (r.importance != DEFAULT_IMPORTANCE) {
1923 PackagePreferences.put("importance",
1924 NotificationListenerService.Ranking.importanceToString(
1925 r.importance));
1926 }
1927 if (r.priority != DEFAULT_PRIORITY) {
1928 PackagePreferences.put("priority",
1929 Notification.priorityToString(r.priority));
1930 }
1931 if (r.visibility != DEFAULT_VISIBILITY) {
1932 PackagePreferences.put("visibility",
1933 Notification.visibilityToString(r.visibility));
1934 }
1935 if (r.showBadge != DEFAULT_SHOW_BADGE) {
1936 PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
1937 }
1938 JSONArray channels = new JSONArray();
1939 for (NotificationChannel channel : r.channels.values()) {
1940 channels.put(channel.toJson());
1941 }
1942 PackagePreferences.put("channels", channels);
1943 JSONArray groups = new JSONArray();
1944 for (NotificationChannelGroup group : r.groups.values()) {
1945 groups.put(group.toJson());
1946 }
1947 PackagePreferences.put("groups", groups);
1948 } catch (JSONException e) {
1949 // pass
1950 }
1951 PackagePreferencess.put(PackagePreferences);
1952 }
1953 }
1954 }
1955 try {
1956 ranking.put("PackagePreferencess", PackagePreferencess);
1957 } catch (JSONException e) {
1958 // pass
1959 }
1960 return ranking;
1961 }
1962
1963 /**
1964 * Dump only the ban information as structured JSON for the stats collector.
1965 *
1966 * This is intentionally redundant with {#link dumpJson} because the old
1967 * scraper will expect this format.
1968 *
1969 * @param filter
1970 * @return
1971 */
1972 public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
1973 JSONArray bans = new JSONArray();
1974 Map<Integer, String> packageBans = getPackageBans();
1975 for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
1976 final int userId = UserHandle.getUserId(ban.getKey());
1977 final String packageName = ban.getValue();
1978 if (filter == null || filter.matches(packageName)) {
1979 JSONObject banJson = new JSONObject();
1980 try {
1981 banJson.put("userId", userId);
1982 banJson.put("packageName", packageName);
1983 } catch (JSONException e) {
1984 e.printStackTrace();
1985 }
1986 bans.put(banJson);
1987 }
1988 }
1989 return bans;
1990 }
1991
1992 public Map<Integer, String> getPackageBans() {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001993 synchronized (mPackagePreferences) {
1994 final int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001995 ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
1996 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04001997 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04001998 if (r.importance == IMPORTANCE_NONE) {
1999 packageBans.put(r.uid, r.pkg);
2000 }
2001 }
2002
2003 return packageBans;
2004 }
2005 }
2006
2007 /**
2008 * Dump only the channel information as structured JSON for the stats collector.
2009 *
2010 * This is intentionally redundant with {#link dumpJson} because the old
2011 * scraper will expect this format.
2012 *
2013 * @param filter
2014 * @return
2015 */
2016 public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
2017 JSONArray channels = new JSONArray();
2018 Map<String, Integer> packageChannels = getPackageChannels();
2019 for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) {
2020 final String packageName = channelCount.getKey();
2021 if (filter == null || filter.matches(packageName)) {
2022 JSONObject channelCountJson = new JSONObject();
2023 try {
2024 channelCountJson.put("packageName", packageName);
2025 channelCountJson.put("channelCount", channelCount.getValue());
2026 } catch (JSONException e) {
2027 e.printStackTrace();
2028 }
2029 channels.put(channelCountJson);
2030 }
2031 }
2032 return channels;
2033 }
2034
2035 private Map<String, Integer> getPackageChannels() {
2036 ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002037 synchronized (mPackagePreferences) {
2038 for (int i = 0; i < mPackagePreferences.size(); i++) {
2039 final PackagePreferences r = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002040 int channelCount = 0;
2041 for (int j = 0; j < r.channels.size(); j++) {
2042 if (!r.channels.valueAt(j).isDeleted()) {
2043 channelCount++;
2044 }
2045 }
2046 packageChannels.put(r.pkg, channelCount);
2047 }
2048 }
2049 return packageChannels;
2050 }
2051
Beverly0479cde22018-11-09 11:05:34 -05002052 /**
2053 * Called when user switches
2054 */
2055 public void onUserSwitched(int userId) {
2056 syncChannelsBypassingDnd(userId);
2057 }
2058
2059 /**
2060 * Called when user is unlocked
2061 */
2062 public void onUserUnlocked(int userId) {
2063 syncChannelsBypassingDnd(userId);
2064 }
2065
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002066 public void onUserRemoved(int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002067 synchronized (mPackagePreferences) {
2068 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002069 for (int i = N - 1; i >= 0; i--) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002070 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002071 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002072 mPackagePreferences.removeAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002073 }
2074 }
2075 }
2076 }
2077
2078 protected void onLocaleChanged(Context context, int userId) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002079 synchronized (mPackagePreferences) {
2080 int N = mPackagePreferences.size();
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002081 for (int i = 0; i < N; i++) {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002082 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002083 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
2084 if (PackagePreferences.channels.containsKey(
2085 NotificationChannel.DEFAULT_CHANNEL_ID)) {
2086 PackagePreferences.channels.get(
2087 NotificationChannel.DEFAULT_CHANNEL_ID).setName(
2088 context.getResources().getString(
2089 R.string.default_notification_channel_label));
2090 }
2091 }
2092 }
2093 }
2094 }
2095
Julia Reynolds996c7c12019-05-24 10:25:33 -04002096 public boolean onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002097 int[] uidList) {
2098 if (pkgList == null || pkgList.length == 0) {
Julia Reynolds996c7c12019-05-24 10:25:33 -04002099 return false; // nothing to do
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002100 }
2101 boolean updated = false;
2102 if (removingPackage) {
2103 // Remove notification settings for uninstalled package
2104 int size = Math.min(pkgList.length, uidList.length);
2105 for (int i = 0; i < size; i++) {
2106 final String pkg = pkgList[i];
2107 final int uid = uidList[i];
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002108 synchronized (mPackagePreferences) {
2109 mPackagePreferences.remove(packagePreferencesKey(pkg, uid));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002110 }
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002111 mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002112 updated = true;
2113 }
2114 } else {
2115 for (String pkg : pkgList) {
2116 // Package install
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002117 final PackagePreferences r =
2118 mRestoredWithoutUids.get(unrestoredPackageKey(pkg, changeUserId));
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002119 if (r != null) {
2120 try {
2121 r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002122 mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId));
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002123 synchronized (mPackagePreferences) {
2124 mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002125 }
2126 updated = true;
2127 } catch (PackageManager.NameNotFoundException e) {
2128 // noop
2129 }
2130 }
2131 // Package upgrade
2132 try {
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002133 synchronized (mPackagePreferences) {
Julia Reynolds5c399c62019-04-08 14:42:53 -04002134 PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg,
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002135 mPm.getPackageUidAsUser(pkg, changeUserId));
2136 if (fullPackagePreferences != null) {
Julia Reynolds996c7c12019-05-24 10:25:33 -04002137 updated |= createDefaultChannelIfNeededLocked(fullPackagePreferences);
2138 updated |= deleteDefaultChannelIfNeededLocked(fullPackagePreferences);
Julia Reynoldsb24c62c2018-09-10 10:05:15 -04002139 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002140 }
2141 } catch (PackageManager.NameNotFoundException e) {
2142 }
2143 }
2144 }
2145
2146 if (updated) {
2147 updateConfig();
2148 }
Julia Reynolds996c7c12019-05-24 10:25:33 -04002149 return updated;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002150 }
2151
Julia Reynolds7af51c52019-04-19 11:08:27 -04002152 public void clearData(String pkg, int uid) {
2153 synchronized (mPackagePreferences) {
2154 PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
2155 if (p != null) {
2156 p.channels = new ArrayMap<>();
2157 p.groups = new ArrayMap<>();
2158 p.delegate = null;
2159 p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Mady Mellora92268c2020-03-09 17:25:08 -07002160 p.bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
Julia Reynolds7af51c52019-04-19 11:08:27 -04002161 p.importance = DEFAULT_IMPORTANCE;
2162 p.priority = DEFAULT_PRIORITY;
2163 p.visibility = DEFAULT_VISIBILITY;
2164 p.showBadge = DEFAULT_SHOW_BADGE;
2165 }
2166 }
2167 }
2168
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002169 private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
2170 return new LogMaker(
2171 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2172 .ACTION_NOTIFICATION_CHANNEL)
2173 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
2174 .setPackageName(pkg)
2175 .addTaggedData(
2176 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2177 .FIELD_NOTIFICATION_CHANNEL_ID,
2178 channel.getId())
2179 .addTaggedData(
2180 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2181 .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
2182 channel.getImportance());
2183 }
2184
2185 private LogMaker getChannelGroupLog(String groupId, String pkg) {
2186 return new LogMaker(
2187 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2188 .ACTION_NOTIFICATION_CHANNEL_GROUP)
2189 .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
2190 .addTaggedData(
2191 com.android.internal.logging.nano.MetricsProto.MetricsEvent
2192 .FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
2193 groupId)
2194 .setPackageName(pkg);
2195 }
2196
Julia Reynolds4509ce72019-01-31 13:12:43 -05002197 public void updateBubblesEnabled() {
Lyn Han4463f842019-07-09 15:27:28 -07002198 final boolean newValue = Settings.Global.getInt(mContext.getContentResolver(),
2199 Settings.Global.NOTIFICATION_BUBBLES,
Mady Mellora92268c2020-03-09 17:25:08 -07002200 DEFAULT_GLOBAL_ALLOW_BUBBLE ? 1 : 0) == 1;
2201 if (newValue != mBubblesEnabledGlobally) {
2202 mBubblesEnabledGlobally = newValue;
Julia Reynolds4509ce72019-01-31 13:12:43 -05002203 updateConfig();
2204 }
2205 }
2206
Lyn Han4463f842019-07-09 15:27:28 -07002207 public boolean bubblesEnabled() {
Mady Mellora92268c2020-03-09 17:25:08 -07002208 return mBubblesEnabledGlobally;
Julia Reynolds4509ce72019-01-31 13:12:43 -05002209 }
2210
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002211 public void updateBadgingEnabled() {
2212 if (mBadgingEnabled == null) {
2213 mBadgingEnabled = new SparseBooleanArray();
2214 }
2215 boolean changed = false;
2216 // update the cached values
2217 for (int index = 0; index < mBadgingEnabled.size(); index++) {
2218 int userId = mBadgingEnabled.keyAt(index);
2219 final boolean oldValue = mBadgingEnabled.get(userId);
2220 final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2221 Settings.Secure.NOTIFICATION_BADGING,
2222 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
2223 mBadgingEnabled.put(userId, newValue);
2224 changed |= oldValue != newValue;
2225 }
2226 if (changed) {
2227 updateConfig();
2228 }
2229 }
2230
2231 public boolean badgingEnabled(UserHandle userHandle) {
2232 int userId = userHandle.getIdentifier();
2233 if (userId == UserHandle.USER_ALL) {
2234 return false;
2235 }
2236 if (mBadgingEnabled.indexOfKey(userId) < 0) {
2237 mBadgingEnabled.put(userId,
2238 Settings.Secure.getIntForUser(mContext.getContentResolver(),
2239 Settings.Secure.NOTIFICATION_BADGING,
2240 DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
2241 }
2242 return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
2243 }
2244
2245 private void updateConfig() {
2246 mRankingHandler.requestSort();
2247 }
2248
2249 private static String packagePreferencesKey(String pkg, int uid) {
2250 return pkg + "|" + uid;
2251 }
2252
Julia Reynoldsdb34c1b2019-11-08 10:58:15 -05002253 private static String unrestoredPackageKey(String pkg, @UserIdInt int userId) {
2254 return pkg + "|" + userId;
2255 }
2256
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002257 private static class PackagePreferences {
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002258 String pkg;
2259 int uid = UNKNOWN_UID;
2260 int importance = DEFAULT_IMPORTANCE;
2261 int priority = DEFAULT_PRIORITY;
2262 int visibility = DEFAULT_VISIBILITY;
2263 boolean showBadge = DEFAULT_SHOW_BADGE;
Mady Mellora92268c2020-03-09 17:25:08 -07002264 int bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002265 int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Julia Reynolds0c245002019-03-27 16:10:11 -04002266 // these fields are loaded on boot from a different source of truth and so are not
2267 // written to notification policy xml
Julia Reynolds413ba842019-01-11 10:38:08 -05002268 boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
Julia Reynolds72b28442019-11-12 11:43:39 -05002269 List<String> oemLockedChannels = new ArrayList<>();
Julia Reynolds0c245002019-03-27 16:10:11 -04002270 boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002271
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04002272 Delegate delegate = null;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002273 ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
2274 Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
Julia Reynoldsa7ba45a2018-08-29 09:07:52 -04002275
2276 public boolean isValidDelegate(String pkg, int uid) {
2277 return delegate != null && delegate.isAllowed(pkg, uid);
2278 }
2279 }
2280
2281 private static class Delegate {
2282 static final boolean DEFAULT_ENABLED = true;
2283 static final boolean DEFAULT_USER_ALLOWED = true;
2284 String mPkg;
2285 int mUid = UNKNOWN_UID;
2286 boolean mEnabled = DEFAULT_ENABLED;
2287 boolean mUserAllowed = DEFAULT_USER_ALLOWED;
2288
2289 Delegate(String pkg, int uid, boolean enabled, boolean userAllowed) {
2290 mPkg = pkg;
2291 mUid = uid;
2292 mEnabled = enabled;
2293 mUserAllowed = userAllowed;
2294 }
2295
2296 public boolean isAllowed(String pkg, int uid) {
2297 if (pkg == null || uid == UNKNOWN_UID) {
2298 return false;
2299 }
2300 return pkg.equals(mPkg)
2301 && uid == mUid
2302 && (mUserAllowed && mEnabled);
2303 }
Aaron Heuckrothe5bec152018-07-09 16:26:09 -04002304 }
2305}