blob: 827482ff2a4ba56f1ecfad08be3fa1248a03601e [file] [log] [blame]
Chris Wren54bbef42014-07-09 18:37:56 -04001/**
2 * Copyright (c) 2014, 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 */
16package com.android.server.notification;
17
18import android.app.Notification;
19import android.content.Context;
John Spurlock35ef0a62015-05-28 11:24:10 -040020import android.content.pm.PackageManager;
21import android.content.pm.PackageManager.NameNotFoundException;
Chris Wren54bbef42014-07-09 18:37:56 -040022import android.os.UserHandle;
Julia Reynolds5d25ee72015-11-20 15:38:20 -050023import android.service.notification.NotificationListenerService.Ranking;
Chris Wren54bbef42014-07-09 18:37:56 -040024import android.text.TextUtils;
25import android.util.ArrayMap;
Chris Wren54bbef42014-07-09 18:37:56 -040026import android.util.Slog;
John Spurlock1d881a12015-03-18 19:21:54 -040027
Julia Reynolds233a5f92015-10-19 13:51:23 -040028import com.android.internal.R;
29
Chris Wren54bbef42014-07-09 18:37:56 -040030import org.xmlpull.v1.XmlPullParser;
31import org.xmlpull.v1.XmlPullParserException;
32import org.xmlpull.v1.XmlSerializer;
33
34import java.io.IOException;
35import java.io.PrintWriter;
36import java.util.ArrayList;
37import java.util.Collections;
Julia Reynolds233a5f92015-10-19 13:51:23 -040038import java.util.List;
39import java.util.Map;
Chris Wren54bbef42014-07-09 18:37:56 -040040
41public class RankingHelper implements RankingConfig {
42 private static final String TAG = "RankingHelper";
Chris Wren54bbef42014-07-09 18:37:56 -040043
44 private static final int XML_VERSION = 1;
45
46 private static final String TAG_RANKING = "ranking";
47 private static final String TAG_PACKAGE = "package";
48 private static final String ATT_VERSION = "version";
Julia Reynolds233a5f92015-10-19 13:51:23 -040049 private static final String TAG_TOPIC = "topic";
Chris Wren54bbef42014-07-09 18:37:56 -040050
51 private static final String ATT_NAME = "name";
52 private static final String ATT_UID = "uid";
53 private static final String ATT_PRIORITY = "priority";
Chris Wren3ad4e3a2014-09-02 17:23:51 -040054 private static final String ATT_VISIBILITY = "visibility";
Julia Reynolds5d25ee72015-11-20 15:38:20 -050055 private static final String ATT_IMPORTANCE = "importance";
Julia Reynolds233a5f92015-10-19 13:51:23 -040056 private static final String ATT_TOPIC_ID = "id";
57 private static final String ATT_TOPIC_LABEL = "label";
Chris Wren54bbef42014-07-09 18:37:56 -040058
John Spurlock1d881a12015-03-18 19:21:54 -040059 private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
Julia Reynolds5d25ee72015-11-20 15:38:20 -050060 private static final int DEFAULT_VISIBILITY = Ranking.VISIBILITY_NO_OVERRIDE;
61 private static final int DEFAULT_IMPORTANCE = Ranking.IMPORTANCE_UNSPECIFIED;
John Spurlock1d881a12015-03-18 19:21:54 -040062
Chris Wren54bbef42014-07-09 18:37:56 -040063 private final NotificationSignalExtractor[] mSignalExtractors;
Chris Wren1031c972014-07-23 13:11:45 +000064 private final NotificationComparator mPreliminaryComparator = new NotificationComparator();
Christoph Studercd4adf82014-08-19 17:50:49 +020065 private final GlobalSortKeyComparator mFinalComparator = new GlobalSortKeyComparator();
Chris Wren54bbef42014-07-09 18:37:56 -040066
John Spurlock1d881a12015-03-18 19:21:54 -040067 private final ArrayMap<String, Record> mRecords = new ArrayMap<>(); // pkg|uid => Record
68 private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp = new ArrayMap<>();
John Spurlock35ef0a62015-05-28 11:24:10 -040069 private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record
Chris Wren54bbef42014-07-09 18:37:56 -040070
71 private final Context mContext;
Chris Wren51017d02015-12-15 15:34:46 -050072 private final RankingHandler mRankingHandler;
Chris Wren54bbef42014-07-09 18:37:56 -040073
Chris Wren51017d02015-12-15 15:34:46 -050074 public RankingHelper(Context context, RankingHandler rankingHandler,
75 NotificationUsageStats usageStats, String[] extractorNames) {
Chris Wren54bbef42014-07-09 18:37:56 -040076 mContext = context;
77 mRankingHandler = rankingHandler;
Chris Wren54bbef42014-07-09 18:37:56 -040078
79 final int N = extractorNames.length;
80 mSignalExtractors = new NotificationSignalExtractor[N];
81 for (int i = 0; i < N; i++) {
82 try {
83 Class<?> extractorClass = mContext.getClassLoader().loadClass(extractorNames[i]);
84 NotificationSignalExtractor extractor =
85 (NotificationSignalExtractor) extractorClass.newInstance();
Chris Wren5eab2b72015-06-16 13:56:22 -040086 extractor.initialize(mContext, usageStats);
Chris Wren54bbef42014-07-09 18:37:56 -040087 extractor.setConfig(this);
88 mSignalExtractors[i] = extractor;
89 } catch (ClassNotFoundException e) {
90 Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e);
91 } catch (InstantiationException e) {
92 Slog.w(TAG, "Couldn't instantiate extractor " + extractorNames[i] + ".", e);
93 } catch (IllegalAccessException e) {
94 Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
95 }
96 }
97 }
98
John Spurlock1d881a12015-03-18 19:21:54 -040099 @SuppressWarnings("unchecked")
John Spurlock2b122f42014-08-27 16:29:47 -0400100 public <T extends NotificationSignalExtractor> T findExtractor(Class<T> extractorClass) {
101 final int N = mSignalExtractors.length;
102 for (int i = 0; i < N; i++) {
103 final NotificationSignalExtractor extractor = mSignalExtractors[i];
104 if (extractorClass.equals(extractor.getClass())) {
105 return (T) extractor;
106 }
107 }
108 return null;
109 }
110
Chris Wren54bbef42014-07-09 18:37:56 -0400111 public void extractSignals(NotificationRecord r) {
112 final int N = mSignalExtractors.length;
113 for (int i = 0; i < N; i++) {
114 NotificationSignalExtractor extractor = mSignalExtractors[i];
115 try {
116 RankingReconsideration recon = extractor.process(r);
117 if (recon != null) {
Chris Wren51017d02015-12-15 15:34:46 -0500118 mRankingHandler.requestReconsideration(recon);
Chris Wren54bbef42014-07-09 18:37:56 -0400119 }
120 } catch (Throwable t) {
121 Slog.w(TAG, "NotificationSignalExtractor failed.", t);
122 }
123 }
124 }
125
John Spurlock35ef0a62015-05-28 11:24:10 -0400126 public void readXml(XmlPullParser parser, boolean forRestore)
127 throws XmlPullParserException, IOException {
128 final PackageManager pm = mContext.getPackageManager();
Chris Wren54bbef42014-07-09 18:37:56 -0400129 int type = parser.getEventType();
130 if (type != XmlPullParser.START_TAG) return;
131 String tag = parser.getName();
132 if (!TAG_RANKING.equals(tag)) return;
John Spurlock1d881a12015-03-18 19:21:54 -0400133 mRecords.clear();
John Spurlock35ef0a62015-05-28 11:24:10 -0400134 mRestoredWithoutUids.clear();
Chris Wren54bbef42014-07-09 18:37:56 -0400135 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
136 tag = parser.getName();
137 if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
138 return;
139 }
140 if (type == XmlPullParser.START_TAG) {
141 if (TAG_PACKAGE.equals(tag)) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400142 int uid = safeInt(parser, ATT_UID, Record.UNKNOWN_UID);
John Spurlock1d881a12015-03-18 19:21:54 -0400143 int priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
John Spurlock1d881a12015-03-18 19:21:54 -0400144 int vis = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
Chris Wren54bbef42014-07-09 18:37:56 -0400145 String name = parser.getAttributeValue(null, ATT_NAME);
146
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400147 if (!TextUtils.isEmpty(name)) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400148 if (forRestore) {
149 try {
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700150 //TODO: http://b/22388012
Jeff Sharkeye06b4d12016-01-06 14:51:50 -0700151 uid = pm.getPackageUidAsUser(name, UserHandle.USER_SYSTEM);
John Spurlock35ef0a62015-05-28 11:24:10 -0400152 } catch (NameNotFoundException e) {
153 // noop
154 }
155 }
156 Record r = null;
157 if (uid == Record.UNKNOWN_UID) {
158 r = mRestoredWithoutUids.get(name);
159 if (r == null) {
160 r = new Record();
161 mRestoredWithoutUids.put(name, r);
162 }
163 } else {
164 r = getOrCreateRecord(name, uid);
165 }
Julia Reynoldsbe8fdee2015-12-18 09:04:34 -0500166 r.importance = safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
Julia Reynolds92d456e2016-01-25 16:36:59 -0500167 r.priority = priority;
168 r.visibility = vis;
Julia Reynolds233a5f92015-10-19 13:51:23 -0400169
170 // Migrate package level settings to the default topic.
171 // Might be overwritten by parseTopics.
172 Topic defaultTopic = r.topics.get(Notification.TOPIC_DEFAULT);
173 defaultTopic.priority = priority;
174 defaultTopic.visibility = vis;
175
176 parseTopics(r, parser);
Chris Wren54bbef42014-07-09 18:37:56 -0400177 }
178 }
179 }
180 }
181 throw new IllegalStateException("Failed to reach END_DOCUMENT");
182 }
183
Julia Reynolds233a5f92015-10-19 13:51:23 -0400184 public void parseTopics(Record r, XmlPullParser parser)
185 throws XmlPullParserException, IOException {
186 final int innerDepth = parser.getDepth();
187 int type;
188 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
189 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
190 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
191 continue;
192 }
193
194 String tagName = parser.getName();
195 if (TAG_TOPIC.equals(tagName)) {
196 int priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
197 int vis = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500198 int importance = safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
Julia Reynolds233a5f92015-10-19 13:51:23 -0400199 String id = parser.getAttributeValue(null, ATT_TOPIC_ID);
200 CharSequence label = parser.getAttributeValue(null, ATT_TOPIC_LABEL);
201
202 if (!TextUtils.isEmpty(id)) {
203 Topic topic = new Topic(new Notification.Topic(id, label));
204
205 if (priority != DEFAULT_PRIORITY) {
206 topic.priority = priority;
207 }
208 if (vis != DEFAULT_VISIBILITY) {
209 topic.visibility = vis;
210 }
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500211 if (importance != DEFAULT_IMPORTANCE) {
212 topic.importance = importance;
213 }
Julia Reynolds233a5f92015-10-19 13:51:23 -0400214 r.topics.put(id, topic);
215 }
216 }
217 }
218 }
219
John Spurlock1d881a12015-03-18 19:21:54 -0400220 private static String recordKey(String pkg, int uid) {
221 return pkg + "|" + uid;
222 }
223
224 private Record getOrCreateRecord(String pkg, int uid) {
225 final String key = recordKey(pkg, uid);
226 Record r = mRecords.get(key);
227 if (r == null) {
228 r = new Record();
229 r.pkg = pkg;
230 r.uid = uid;
Julia Reynoldsda303542015-11-23 14:00:20 -0500231 r.topics.put(Notification.TOPIC_DEFAULT, new Topic(createDefaultTopic()));
John Spurlock1d881a12015-03-18 19:21:54 -0400232 mRecords.put(key, r);
233 }
234 return r;
235 }
236
John Spurlock35ef0a62015-05-28 11:24:10 -0400237 public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
Chris Wren54bbef42014-07-09 18:37:56 -0400238 out.startTag(null, TAG_RANKING);
239 out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
240
John Spurlock1d881a12015-03-18 19:21:54 -0400241 final int N = mRecords.size();
242 for (int i = 0; i < N; i++) {
243 final Record r = mRecords.valueAt(i);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700244 //TODO: http://b/22388012
245 if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400246 continue;
247 }
John Spurlock1d881a12015-03-18 19:21:54 -0400248 out.startTag(null, TAG_PACKAGE);
249 out.attribute(null, ATT_NAME, r.pkg);
Julia Reynolds92d456e2016-01-25 16:36:59 -0500250 if (r.importance != DEFAULT_IMPORTANCE) {
251 out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
252 }
253 if (r.priority != DEFAULT_PRIORITY) {
254 out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
255 }
256 if (r.visibility != DEFAULT_VISIBILITY) {
257 out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
258 }
Julia Reynolds233a5f92015-10-19 13:51:23 -0400259
John Spurlock35ef0a62015-05-28 11:24:10 -0400260 if (!forBackup) {
261 out.attribute(null, ATT_UID, Integer.toString(r.uid));
262 }
Julia Reynolds233a5f92015-10-19 13:51:23 -0400263
264 writeTopicsXml(out, r);
John Spurlock1d881a12015-03-18 19:21:54 -0400265 out.endTag(null, TAG_PACKAGE);
Chris Wren54bbef42014-07-09 18:37:56 -0400266 }
267 out.endTag(null, TAG_RANKING);
268 }
269
Julia Reynolds233a5f92015-10-19 13:51:23 -0400270 public void writeTopicsXml(XmlSerializer out, Record r) throws IOException {
271 for (Topic t : r.topics.values()) {
272 out.startTag(null, TAG_TOPIC);
273 out.attribute(null, ATT_TOPIC_ID, t.topic.getId());
274 out.attribute(null, ATT_TOPIC_LABEL, t.topic.getLabel().toString());
275 if (t.priority != DEFAULT_PRIORITY) {
276 out.attribute(null, ATT_PRIORITY, Integer.toString(t.priority));
277 }
278 if (t.visibility != DEFAULT_VISIBILITY) {
279 out.attribute(null, ATT_VISIBILITY, Integer.toString(t.visibility));
280 }
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500281 if (t.importance != DEFAULT_IMPORTANCE) {
282 out.attribute(null, ATT_IMPORTANCE, Integer.toString(t.importance));
283 }
Julia Reynolds233a5f92015-10-19 13:51:23 -0400284 out.endTag(null, TAG_TOPIC);
285 }
286 }
287
Chris Wren54bbef42014-07-09 18:37:56 -0400288 private void updateConfig() {
289 final int N = mSignalExtractors.length;
290 for (int i = 0; i < N; i++) {
291 mSignalExtractors[i].setConfig(this);
292 }
Chris Wren51017d02015-12-15 15:34:46 -0500293 mRankingHandler.requestSort();
Chris Wren54bbef42014-07-09 18:37:56 -0400294 }
295
296 public void sort(ArrayList<NotificationRecord> notificationList) {
Chris Wren1031c972014-07-23 13:11:45 +0000297 final int N = notificationList.size();
Christoph Studercd4adf82014-08-19 17:50:49 +0200298 // clear global sort keys
Chris Wren1031c972014-07-23 13:11:45 +0000299 for (int i = N - 1; i >= 0; i--) {
Christoph Studercd4adf82014-08-19 17:50:49 +0200300 notificationList.get(i).setGlobalSortKey(null);
Chris Wren1031c972014-07-23 13:11:45 +0000301 }
302
Christoph Studer85374052014-10-08 11:19:55 -0700303 // rank each record individually
304 Collections.sort(notificationList, mPreliminaryComparator);
Christoph Studercd4adf82014-08-19 17:50:49 +0200305
306 synchronized (mProxyByGroupTmp) {
307 // record individual ranking result and nominate proxies for each group
308 for (int i = N - 1; i >= 0; i--) {
309 final NotificationRecord record = notificationList.get(i);
310 record.setAuthoritativeRank(i);
311 final String groupKey = record.getGroupKey();
312 boolean isGroupSummary = record.getNotification().isGroupSummary();
313 if (isGroupSummary || !mProxyByGroupTmp.containsKey(groupKey)) {
314 mProxyByGroupTmp.put(groupKey, record);
315 }
316 }
317 // assign global sort key:
318 // is_recently_intrusive:group_rank:is_group_summary:group_sort_key:rank
319 for (int i = 0; i < N; i++) {
320 final NotificationRecord record = notificationList.get(i);
321 NotificationRecord groupProxy = mProxyByGroupTmp.get(record.getGroupKey());
322 String groupSortKey = record.getNotification().getSortKey();
323
324 // We need to make sure the developer provided group sort key (gsk) is handled
325 // correctly:
326 // gsk="" < gsk=non-null-string < gsk=null
327 //
328 // We enforce this by using different prefixes for these three cases.
329 String groupSortKeyPortion;
330 if (groupSortKey == null) {
331 groupSortKeyPortion = "nsk";
332 } else if (groupSortKey.equals("")) {
333 groupSortKeyPortion = "esk";
334 } else {
335 groupSortKeyPortion = "gsk=" + groupSortKey;
336 }
337
338 boolean isGroupSummary = record.getNotification().isGroupSummary();
339 record.setGlobalSortKey(
340 String.format("intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
341 record.isRecentlyIntrusive() ? '0' : '1',
342 groupProxy.getAuthoritativeRank(),
343 isGroupSummary ? '0' : '1',
344 groupSortKeyPortion,
345 record.getAuthoritativeRank()));
346 }
347 mProxyByGroupTmp.clear();
Chris Wren1031c972014-07-23 13:11:45 +0000348 }
Christoph Studercd4adf82014-08-19 17:50:49 +0200349
Chris Wren1031c972014-07-23 13:11:45 +0000350 // Do a second ranking pass, using group proxies
351 Collections.sort(notificationList, mFinalComparator);
Chris Wren54bbef42014-07-09 18:37:56 -0400352 }
353
354 public int indexOf(ArrayList<NotificationRecord> notificationList, NotificationRecord target) {
Chris Wren1031c972014-07-23 13:11:45 +0000355 return Collections.binarySearch(notificationList, target, mFinalComparator);
Chris Wren54bbef42014-07-09 18:37:56 -0400356 }
357
358 private static int safeInt(XmlPullParser parser, String att, int defValue) {
359 final String val = parser.getAttributeValue(null, att);
360 return tryParseInt(val, defValue);
361 }
362
363 private static int tryParseInt(String value, int defValue) {
364 if (TextUtils.isEmpty(value)) return defValue;
365 try {
366 return Integer.valueOf(value);
367 } catch (NumberFormatException e) {
368 return defValue;
369 }
370 }
371
John Spurlock1d881a12015-03-18 19:21:54 -0400372 private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
373 final String val = parser.getAttributeValue(null, att);
374 return tryParseBool(val, defValue);
375 }
376
377 private static boolean tryParseBool(String value, boolean defValue) {
378 if (TextUtils.isEmpty(value)) return defValue;
379 return Boolean.valueOf(value);
380 }
381
Chris Wren54bbef42014-07-09 18:37:56 -0400382 @Override
Julia Reynolds233a5f92015-10-19 13:51:23 -0400383 public List<Notification.Topic> getTopics(String packageName, int uid) {
384 final Record r = getOrCreateRecord(packageName, uid);
385 List<Notification.Topic> topics = new ArrayList<>();
Julia Reynolds92d456e2016-01-25 16:36:59 -0500386 for (Topic t : r.topics.values()) {
Julia Reynolds233a5f92015-10-19 13:51:23 -0400387 topics.add(t.topic);
388 }
389 return topics;
Chris Wren54bbef42014-07-09 18:37:56 -0400390 }
391
Julia Reynolds92d456e2016-01-25 16:36:59 -0500392 /**
393 * Gets priority. If a topic is given, returns the priority of that topic. Otherwise, the
394 * priority of the app.
395 */
Chris Wren54bbef42014-07-09 18:37:56 -0400396 @Override
Julia Reynolds92d456e2016-01-25 16:36:59 -0500397 public int getPriority(String packageName, int uid, Notification.Topic topic) {
Julia Reynolds233a5f92015-10-19 13:51:23 -0400398 final Record r = getOrCreateRecord(packageName, uid);
Julia Reynolds92d456e2016-01-25 16:36:59 -0500399 if (topic == null) {
400 return r.priority;
401 }
Julia Reynolds233a5f92015-10-19 13:51:23 -0400402 return getOrCreateTopic(r, topic).priority;
403 }
404
Julia Reynolds92d456e2016-01-25 16:36:59 -0500405 /**
406 * Sets priority. If a topic is given, sets the priority of that topic. If not,
407 * sets the default priority for all new topics that appear in the future, and resets
408 * the priority of all current topics.
409 */
Julia Reynolds233a5f92015-10-19 13:51:23 -0400410 @Override
Julia Reynolds92d456e2016-01-25 16:36:59 -0500411 public void setPriority(String packageName, int uid, Notification.Topic topic,
Julia Reynolds233a5f92015-10-19 13:51:23 -0400412 int priority) {
413 final Record r = getOrCreateRecord(packageName, uid);
Julia Reynolds92d456e2016-01-25 16:36:59 -0500414 if (topic == null) {
415 r.priority = priority;
416 for (Topic t : r.topics.values()) {
417 t.priority = priority;
418 }
419 } else {
420 getOrCreateTopic(r, topic).priority = priority;
421 }
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500422 updateConfig();
423 }
424
Julia Reynoldsa07af882015-12-17 08:32:48 -0500425 /**
Julia Reynolds92d456e2016-01-25 16:36:59 -0500426 * Gets visual override. If a topic is given, returns the override of that topic. Otherwise, the
427 * override of the app.
428 */
429 @Override
430 public int getVisibilityOverride(String packageName, int uid, Notification.Topic topic) {
431 final Record r = getOrCreateRecord(packageName, uid);
432 if (topic == null) {
433 return r.visibility;
434 }
435 return getOrCreateTopic(r, topic).visibility;
436 }
437
438 /**
439 * Sets visibility override. If a topic is given, sets the override of that topic. If not,
440 * sets the default override for all new topics that appear in the future, and resets
441 * the override of all current topics.
442 */
443 @Override
444 public void setVisibilityOverride(String pkgName, int uid, Notification.Topic topic,
445 int visibility) {
446 final Record r = getOrCreateRecord(pkgName, uid);
447 if (topic == null) {
448 r.visibility = visibility;
449 for (Topic t : r.topics.values()) {
450 t.visibility = visibility;
451 }
452 } else {
453 getOrCreateTopic(r, topic).visibility = visibility;
454 }
455 updateConfig();
456 }
457
458 /**
459 * Gets importance. If a topic is given, returns the importance of that topic. Otherwise, the
460 * importance of the app.
461 */
462 @Override
463 public int getImportance(String packageName, int uid, Notification.Topic topic) {
464 final Record r = getOrCreateRecord(packageName, uid);
465 if (topic == null) {
466 return r.importance;
467 }
468 return getOrCreateTopic(r, topic).importance;
469 }
470
471 /**
472 * Sets importance. If a topic is given, sets the importance of that topic. If not, sets the
473 * default importance for all new topics that appear in the future, and resets
Julia Reynolds85aa6cb2016-01-08 17:49:11 -0500474 * the importance of all current topics (unless the app is being blocked).
Julia Reynoldsa07af882015-12-17 08:32:48 -0500475 */
476 @Override
Julia Reynolds92d456e2016-01-25 16:36:59 -0500477 public void setImportance(String pkgName, int uid, Notification.Topic topic,
478 int importance) {
Julia Reynoldsa07af882015-12-17 08:32:48 -0500479 final Record r = getOrCreateRecord(pkgName, uid);
Julia Reynolds92d456e2016-01-25 16:36:59 -0500480 if (topic == null) {
481 r.importance = importance;
482 if (Ranking.IMPORTANCE_NONE != importance) {
483 for (Topic t : r.topics.values()) {
484 t.importance = importance;
485 }
Julia Reynolds85aa6cb2016-01-08 17:49:11 -0500486 }
Julia Reynolds92d456e2016-01-25 16:36:59 -0500487 } else {
488 getOrCreateTopic(r, topic).importance = importance;
Julia Reynoldsa07af882015-12-17 08:32:48 -0500489 }
490 updateConfig();
491 }
492
Julia Reynoldsbe8fdee2015-12-18 09:04:34 -0500493 @Override
494 public boolean doesAppUseTopics(String pkgName, int uid) {
495 final Record r = getOrCreateRecord(pkgName, uid);
496 int numTopics = r.topics.size();
497 if (numTopics == 0
498 || (numTopics == 1 && r.topics.containsKey(Notification.TOPIC_DEFAULT))) {
499 return false;
500 } else {
501 return true;
502 }
503 }
504
Julia Reynolds233a5f92015-10-19 13:51:23 -0400505 private Topic getOrCreateTopic(Record r, Notification.Topic topic) {
Julia Reynoldsda303542015-11-23 14:00:20 -0500506 if (topic == null) {
507 topic = createDefaultTopic();
508 }
Julia Reynolds233a5f92015-10-19 13:51:23 -0400509 Topic t = r.topics.get(topic.getId());
510 if (t != null) {
511 return t;
512 } else {
513 t = new Topic(topic);
Julia Reynoldsa07af882015-12-17 08:32:48 -0500514 t.importance = r.importance;
Julia Reynolds92d456e2016-01-25 16:36:59 -0500515 t.priority = r.priority;
516 t.visibility = r.visibility;
Julia Reynolds233a5f92015-10-19 13:51:23 -0400517 r.topics.put(topic.getId(), t);
518 return t;
519 }
520 }
521
Julia Reynoldsda303542015-11-23 14:00:20 -0500522 private Notification.Topic createDefaultTopic() {
523 return new Notification.Topic(Notification.TOPIC_DEFAULT,
524 mContext.getString(R.string.default_notification_topic_label));
525 }
526
Chris Wren54bbef42014-07-09 18:37:56 -0400527 public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
528 if (filter == null) {
529 final int N = mSignalExtractors.length;
530 pw.print(prefix);
531 pw.print("mSignalExtractors.length = ");
532 pw.println(N);
533 for (int i = 0; i < N; i++) {
534 pw.print(prefix);
535 pw.print(" ");
536 pw.println(mSignalExtractors[i]);
537 }
538 }
Chris Wren54bbef42014-07-09 18:37:56 -0400539 if (filter == null) {
540 pw.print(prefix);
John Spurlock1d881a12015-03-18 19:21:54 -0400541 pw.println("per-package config:");
Chris Wren54bbef42014-07-09 18:37:56 -0400542 }
Julia Reynolds85aa6cb2016-01-08 17:49:11 -0500543 pw.println("Records:");
John Spurlock35ef0a62015-05-28 11:24:10 -0400544 dumpRecords(pw, prefix, filter, mRecords);
Julia Reynolds85aa6cb2016-01-08 17:49:11 -0500545 pw.println("Restored without uid:");
John Spurlock35ef0a62015-05-28 11:24:10 -0400546 dumpRecords(pw, prefix, filter, mRestoredWithoutUids);
547 }
548
549 private static void dumpRecords(PrintWriter pw, String prefix,
550 NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) {
551 final int N = records.size();
Chris Wren54bbef42014-07-09 18:37:56 -0400552 for (int i = 0; i < N; i++) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400553 final Record r = records.valueAt(i);
John Spurlock1d881a12015-03-18 19:21:54 -0400554 if (filter == null || filter.matches(r.pkg)) {
555 pw.print(prefix);
556 pw.print(" ");
557 pw.print(r.pkg);
558 pw.print(" (");
John Spurlock35ef0a62015-05-28 11:24:10 -0400559 pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
John Spurlock1d881a12015-03-18 19:21:54 -0400560 pw.print(')');
Julia Reynolds92d456e2016-01-25 16:36:59 -0500561 if (r.importance != DEFAULT_IMPORTANCE) {
562 pw.print(" importance=");
563 pw.print(Ranking.importanceToString(r.importance));
564 }
565 if (r.priority != DEFAULT_PRIORITY) {
566 pw.print(" priority=");
567 pw.print(Ranking.importanceToString(r.priority));
568 }
569 if (r.visibility != DEFAULT_VISIBILITY) {
570 pw.print(" visibility=");
571 pw.print(Ranking.importanceToString(r.visibility));
572 }
John Spurlock1d881a12015-03-18 19:21:54 -0400573 pw.println();
Julia Reynolds233a5f92015-10-19 13:51:23 -0400574 for (Topic t : r.topics.values()) {
575 pw.print(prefix);
576 pw.print(" ");
577 pw.print(" ");
578 pw.print(t.topic.getId());
579 if (t.priority != DEFAULT_PRIORITY) {
580 pw.print(" priority=");
581 pw.print(Notification.priorityToString(t.priority));
582 }
583 if (t.visibility != DEFAULT_VISIBILITY) {
584 pw.print(" visibility=");
585 pw.print(Notification.visibilityToString(t.visibility));
586 }
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500587 if (t.importance != DEFAULT_IMPORTANCE) {
588 pw.print(" importance=");
589 pw.print(Ranking.importanceToString(t.importance));
590 }
Julia Reynolds233a5f92015-10-19 13:51:23 -0400591 pw.println();
592 }
Chris Wren54bbef42014-07-09 18:37:56 -0400593 }
594 }
595 }
John Spurlock1d881a12015-03-18 19:21:54 -0400596
John Spurlock35ef0a62015-05-28 11:24:10 -0400597 public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
598 if (queryReplace || pkgList == null || pkgList.length == 0
599 || mRestoredWithoutUids.isEmpty()) {
600 return; // nothing to do
601 }
602 final PackageManager pm = mContext.getPackageManager();
603 boolean updated = false;
604 for (String pkg : pkgList) {
605 final Record r = mRestoredWithoutUids.get(pkg);
606 if (r != null) {
607 try {
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700608 //TODO: http://b/22388012
Jeff Sharkeye06b4d12016-01-06 14:51:50 -0700609 r.uid = pm.getPackageUidAsUser(r.pkg, UserHandle.USER_SYSTEM);
John Spurlock35ef0a62015-05-28 11:24:10 -0400610 mRestoredWithoutUids.remove(pkg);
611 mRecords.put(recordKey(r.pkg, r.uid), r);
612 updated = true;
613 } catch (NameNotFoundException e) {
614 // noop
615 }
616 }
617 }
618 if (updated) {
619 updateConfig();
620 }
621 }
622
John Spurlock1d881a12015-03-18 19:21:54 -0400623 private static class Record {
John Spurlock35ef0a62015-05-28 11:24:10 -0400624 static int UNKNOWN_UID = UserHandle.USER_NULL;
625
John Spurlock1d881a12015-03-18 19:21:54 -0400626 String pkg;
John Spurlock35ef0a62015-05-28 11:24:10 -0400627 int uid = UNKNOWN_UID;
Julia Reynoldsa07af882015-12-17 08:32:48 -0500628 int importance = DEFAULT_IMPORTANCE;
Julia Reynolds92d456e2016-01-25 16:36:59 -0500629 int priority = DEFAULT_PRIORITY;
630 int visibility = DEFAULT_VISIBILITY;
Julia Reynolds233a5f92015-10-19 13:51:23 -0400631 Map<String, Topic> topics = new ArrayMap<>();
632 }
633
634 private static class Topic {
635 Notification.Topic topic;
John Spurlock1d881a12015-03-18 19:21:54 -0400636 int priority = DEFAULT_PRIORITY;
John Spurlock1d881a12015-03-18 19:21:54 -0400637 int visibility = DEFAULT_VISIBILITY;
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500638 int importance = DEFAULT_IMPORTANCE;
John Spurlock35ef0a62015-05-28 11:24:10 -0400639
Julia Reynolds233a5f92015-10-19 13:51:23 -0400640 public Topic(Notification.Topic topic) {
641 this.topic = topic;
642 }
643 }
Chris Wren54bbef42014-07-09 18:37:56 -0400644}