blob: 90484027446c8015067813755888e0b2ec64098b [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
Chris Wrenacf424a2016-03-15 12:48:55 -040028import com.android.server.notification.NotificationManagerService.DumpFilter;
29
30import org.json.JSONArray;
31import org.json.JSONException;
32import org.json.JSONObject;
Chris Wren54bbef42014-07-09 18:37:56 -040033import org.xmlpull.v1.XmlPullParser;
34import org.xmlpull.v1.XmlPullParserException;
35import org.xmlpull.v1.XmlSerializer;
36
37import java.io.IOException;
38import java.io.PrintWriter;
39import java.util.ArrayList;
40import java.util.Collections;
Chris Wrenacf424a2016-03-15 12:48:55 -040041import java.util.Map;
42import java.util.Map.Entry;
Chris Wren54bbef42014-07-09 18:37:56 -040043
44public class RankingHelper implements RankingConfig {
45 private static final String TAG = "RankingHelper";
Chris Wren54bbef42014-07-09 18:37:56 -040046
47 private static final int XML_VERSION = 1;
48
49 private static final String TAG_RANKING = "ranking";
50 private static final String TAG_PACKAGE = "package";
51 private static final String ATT_VERSION = "version";
52
53 private static final String ATT_NAME = "name";
54 private static final String ATT_UID = "uid";
55 private static final String ATT_PRIORITY = "priority";
Chris Wren3ad4e3a2014-09-02 17:23:51 -040056 private static final String ATT_VISIBILITY = "visibility";
Julia Reynolds5d25ee72015-11-20 15:38:20 -050057 private static final String ATT_IMPORTANCE = "importance";
Julia Reynolds233a5f92015-10-19 13:51:23 -040058 private static final String ATT_TOPIC_ID = "id";
59 private static final String ATT_TOPIC_LABEL = "label";
Chris Wren54bbef42014-07-09 18:37:56 -040060
John Spurlock1d881a12015-03-18 19:21:54 -040061 private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
Julia Reynolds5d25ee72015-11-20 15:38:20 -050062 private static final int DEFAULT_VISIBILITY = Ranking.VISIBILITY_NO_OVERRIDE;
63 private static final int DEFAULT_IMPORTANCE = Ranking.IMPORTANCE_UNSPECIFIED;
John Spurlock1d881a12015-03-18 19:21:54 -040064
Chris Wren54bbef42014-07-09 18:37:56 -040065 private final NotificationSignalExtractor[] mSignalExtractors;
Chris Wren1031c972014-07-23 13:11:45 +000066 private final NotificationComparator mPreliminaryComparator = new NotificationComparator();
Christoph Studercd4adf82014-08-19 17:50:49 +020067 private final GlobalSortKeyComparator mFinalComparator = new GlobalSortKeyComparator();
Chris Wren54bbef42014-07-09 18:37:56 -040068
John Spurlock1d881a12015-03-18 19:21:54 -040069 private final ArrayMap<String, Record> mRecords = new ArrayMap<>(); // pkg|uid => Record
70 private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp = new ArrayMap<>();
John Spurlock35ef0a62015-05-28 11:24:10 -040071 private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record
Chris Wren54bbef42014-07-09 18:37:56 -040072
73 private final Context mContext;
Chris Wren51017d02015-12-15 15:34:46 -050074 private final RankingHandler mRankingHandler;
Chris Wren54bbef42014-07-09 18:37:56 -040075
Chris Wren51017d02015-12-15 15:34:46 -050076 public RankingHelper(Context context, RankingHandler rankingHandler,
77 NotificationUsageStats usageStats, String[] extractorNames) {
Chris Wren54bbef42014-07-09 18:37:56 -040078 mContext = context;
79 mRankingHandler = rankingHandler;
Chris Wren54bbef42014-07-09 18:37:56 -040080
81 final int N = extractorNames.length;
82 mSignalExtractors = new NotificationSignalExtractor[N];
83 for (int i = 0; i < N; i++) {
84 try {
85 Class<?> extractorClass = mContext.getClassLoader().loadClass(extractorNames[i]);
86 NotificationSignalExtractor extractor =
87 (NotificationSignalExtractor) extractorClass.newInstance();
Chris Wren5eab2b72015-06-16 13:56:22 -040088 extractor.initialize(mContext, usageStats);
Chris Wren54bbef42014-07-09 18:37:56 -040089 extractor.setConfig(this);
90 mSignalExtractors[i] = extractor;
91 } catch (ClassNotFoundException e) {
92 Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e);
93 } catch (InstantiationException e) {
94 Slog.w(TAG, "Couldn't instantiate extractor " + extractorNames[i] + ".", e);
95 } catch (IllegalAccessException e) {
96 Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
97 }
98 }
99 }
100
John Spurlock1d881a12015-03-18 19:21:54 -0400101 @SuppressWarnings("unchecked")
John Spurlock2b122f42014-08-27 16:29:47 -0400102 public <T extends NotificationSignalExtractor> T findExtractor(Class<T> extractorClass) {
103 final int N = mSignalExtractors.length;
104 for (int i = 0; i < N; i++) {
105 final NotificationSignalExtractor extractor = mSignalExtractors[i];
106 if (extractorClass.equals(extractor.getClass())) {
107 return (T) extractor;
108 }
109 }
110 return null;
111 }
112
Chris Wren54bbef42014-07-09 18:37:56 -0400113 public void extractSignals(NotificationRecord r) {
114 final int N = mSignalExtractors.length;
115 for (int i = 0; i < N; i++) {
116 NotificationSignalExtractor extractor = mSignalExtractors[i];
117 try {
118 RankingReconsideration recon = extractor.process(r);
119 if (recon != null) {
Chris Wren51017d02015-12-15 15:34:46 -0500120 mRankingHandler.requestReconsideration(recon);
Chris Wren54bbef42014-07-09 18:37:56 -0400121 }
122 } catch (Throwable t) {
123 Slog.w(TAG, "NotificationSignalExtractor failed.", t);
124 }
125 }
126 }
127
John Spurlock35ef0a62015-05-28 11:24:10 -0400128 public void readXml(XmlPullParser parser, boolean forRestore)
129 throws XmlPullParserException, IOException {
130 final PackageManager pm = mContext.getPackageManager();
Chris Wren54bbef42014-07-09 18:37:56 -0400131 int type = parser.getEventType();
132 if (type != XmlPullParser.START_TAG) return;
133 String tag = parser.getName();
134 if (!TAG_RANKING.equals(tag)) return;
John Spurlock1d881a12015-03-18 19:21:54 -0400135 mRecords.clear();
John Spurlock35ef0a62015-05-28 11:24:10 -0400136 mRestoredWithoutUids.clear();
Chris Wren54bbef42014-07-09 18:37:56 -0400137 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
138 tag = parser.getName();
139 if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
140 return;
141 }
142 if (type == XmlPullParser.START_TAG) {
143 if (TAG_PACKAGE.equals(tag)) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400144 int uid = safeInt(parser, ATT_UID, Record.UNKNOWN_UID);
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 Reynoldsef37f282016-02-12 09:11:27 -0500167 r.priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
168 r.visibility = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
Chris Wren54bbef42014-07-09 18:37:56 -0400169 }
170 }
171 }
172 }
173 throw new IllegalStateException("Failed to reach END_DOCUMENT");
174 }
175
John Spurlock1d881a12015-03-18 19:21:54 -0400176 private static String recordKey(String pkg, int uid) {
177 return pkg + "|" + uid;
178 }
179
180 private Record getOrCreateRecord(String pkg, int uid) {
181 final String key = recordKey(pkg, uid);
182 Record r = mRecords.get(key);
183 if (r == null) {
184 r = new Record();
185 r.pkg = pkg;
186 r.uid = uid;
187 mRecords.put(key, r);
188 }
189 return r;
190 }
191
John Spurlock35ef0a62015-05-28 11:24:10 -0400192 public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
Chris Wren54bbef42014-07-09 18:37:56 -0400193 out.startTag(null, TAG_RANKING);
194 out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
195
John Spurlock1d881a12015-03-18 19:21:54 -0400196 final int N = mRecords.size();
197 for (int i = 0; i < N; i++) {
198 final Record r = mRecords.valueAt(i);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700199 //TODO: http://b/22388012
200 if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400201 continue;
202 }
Julia Reynoldsef37f282016-02-12 09:11:27 -0500203 final boolean hasNonDefaultSettings = r.importance != DEFAULT_IMPORTANCE
204 || r.priority != DEFAULT_PRIORITY || r.visibility != DEFAULT_VISIBILITY;
205 if (hasNonDefaultSettings) {
206 out.startTag(null, TAG_PACKAGE);
207 out.attribute(null, ATT_NAME, r.pkg);
208 if (r.importance != DEFAULT_IMPORTANCE) {
209 out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
210 }
211 if (r.priority != DEFAULT_PRIORITY) {
212 out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
213 }
214 if (r.visibility != DEFAULT_VISIBILITY) {
215 out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
216 }
Julia Reynolds233a5f92015-10-19 13:51:23 -0400217
Julia Reynoldsef37f282016-02-12 09:11:27 -0500218 if (!forBackup) {
219 out.attribute(null, ATT_UID, Integer.toString(r.uid));
220 }
Julia Reynolds233a5f92015-10-19 13:51:23 -0400221
Julia Reynoldsef37f282016-02-12 09:11:27 -0500222 out.endTag(null, TAG_PACKAGE);
223 }
Chris Wren54bbef42014-07-09 18:37:56 -0400224 }
225 out.endTag(null, TAG_RANKING);
226 }
227
228 private void updateConfig() {
229 final int N = mSignalExtractors.length;
230 for (int i = 0; i < N; i++) {
231 mSignalExtractors[i].setConfig(this);
232 }
Chris Wren51017d02015-12-15 15:34:46 -0500233 mRankingHandler.requestSort();
Chris Wren54bbef42014-07-09 18:37:56 -0400234 }
235
236 public void sort(ArrayList<NotificationRecord> notificationList) {
Chris Wren1031c972014-07-23 13:11:45 +0000237 final int N = notificationList.size();
Christoph Studercd4adf82014-08-19 17:50:49 +0200238 // clear global sort keys
Chris Wren1031c972014-07-23 13:11:45 +0000239 for (int i = N - 1; i >= 0; i--) {
Christoph Studercd4adf82014-08-19 17:50:49 +0200240 notificationList.get(i).setGlobalSortKey(null);
Chris Wren1031c972014-07-23 13:11:45 +0000241 }
242
Christoph Studer85374052014-10-08 11:19:55 -0700243 // rank each record individually
244 Collections.sort(notificationList, mPreliminaryComparator);
Christoph Studercd4adf82014-08-19 17:50:49 +0200245
246 synchronized (mProxyByGroupTmp) {
247 // record individual ranking result and nominate proxies for each group
248 for (int i = N - 1; i >= 0; i--) {
249 final NotificationRecord record = notificationList.get(i);
250 record.setAuthoritativeRank(i);
251 final String groupKey = record.getGroupKey();
252 boolean isGroupSummary = record.getNotification().isGroupSummary();
253 if (isGroupSummary || !mProxyByGroupTmp.containsKey(groupKey)) {
254 mProxyByGroupTmp.put(groupKey, record);
255 }
256 }
257 // assign global sort key:
258 // is_recently_intrusive:group_rank:is_group_summary:group_sort_key:rank
259 for (int i = 0; i < N; i++) {
260 final NotificationRecord record = notificationList.get(i);
261 NotificationRecord groupProxy = mProxyByGroupTmp.get(record.getGroupKey());
262 String groupSortKey = record.getNotification().getSortKey();
263
264 // We need to make sure the developer provided group sort key (gsk) is handled
265 // correctly:
266 // gsk="" < gsk=non-null-string < gsk=null
267 //
268 // We enforce this by using different prefixes for these three cases.
269 String groupSortKeyPortion;
270 if (groupSortKey == null) {
271 groupSortKeyPortion = "nsk";
272 } else if (groupSortKey.equals("")) {
273 groupSortKeyPortion = "esk";
274 } else {
275 groupSortKeyPortion = "gsk=" + groupSortKey;
276 }
277
278 boolean isGroupSummary = record.getNotification().isGroupSummary();
279 record.setGlobalSortKey(
280 String.format("intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
281 record.isRecentlyIntrusive() ? '0' : '1',
282 groupProxy.getAuthoritativeRank(),
283 isGroupSummary ? '0' : '1',
284 groupSortKeyPortion,
285 record.getAuthoritativeRank()));
286 }
287 mProxyByGroupTmp.clear();
Chris Wren1031c972014-07-23 13:11:45 +0000288 }
Christoph Studercd4adf82014-08-19 17:50:49 +0200289
Chris Wren1031c972014-07-23 13:11:45 +0000290 // Do a second ranking pass, using group proxies
291 Collections.sort(notificationList, mFinalComparator);
Chris Wren54bbef42014-07-09 18:37:56 -0400292 }
293
294 public int indexOf(ArrayList<NotificationRecord> notificationList, NotificationRecord target) {
Chris Wren1031c972014-07-23 13:11:45 +0000295 return Collections.binarySearch(notificationList, target, mFinalComparator);
Chris Wren54bbef42014-07-09 18:37:56 -0400296 }
297
298 private static int safeInt(XmlPullParser parser, String att, int defValue) {
299 final String val = parser.getAttributeValue(null, att);
300 return tryParseInt(val, defValue);
301 }
302
303 private static int tryParseInt(String value, int defValue) {
304 if (TextUtils.isEmpty(value)) return defValue;
305 try {
Narayan Kamatha09b4d22016-04-15 18:32:45 +0100306 return Integer.parseInt(value);
Chris Wren54bbef42014-07-09 18:37:56 -0400307 } catch (NumberFormatException e) {
308 return defValue;
309 }
310 }
311
John Spurlock1d881a12015-03-18 19:21:54 -0400312 private static boolean tryParseBool(String value, boolean defValue) {
313 if (TextUtils.isEmpty(value)) return defValue;
314 return Boolean.valueOf(value);
315 }
316
Julia Reynoldsef37f282016-02-12 09:11:27 -0500317 /**
318 * Gets priority.
319 */
Chris Wren54bbef42014-07-09 18:37:56 -0400320 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -0500321 public int getPriority(String packageName, int uid) {
322 return getOrCreateRecord(packageName, uid).priority;
Julia Reynoldsdd3e86b2016-02-02 10:24:30 -0500323 }
324
Julia Reynolds92d456e2016-01-25 16:36:59 -0500325 /**
Julia Reynoldsef37f282016-02-12 09:11:27 -0500326 * Sets priority.
Julia Reynolds92d456e2016-01-25 16:36:59 -0500327 */
Chris Wren54bbef42014-07-09 18:37:56 -0400328 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -0500329 public void setPriority(String packageName, int uid, int priority) {
330 getOrCreateRecord(packageName, uid).priority = priority;
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500331 updateConfig();
332 }
333
Julia Reynoldsa07af882015-12-17 08:32:48 -0500334 /**
Julia Reynoldsef37f282016-02-12 09:11:27 -0500335 * Gets visual override.
Julia Reynolds92d456e2016-01-25 16:36:59 -0500336 */
337 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -0500338 public int getVisibilityOverride(String packageName, int uid) {
339 return getOrCreateRecord(packageName, uid).visibility;
Julia Reynolds92d456e2016-01-25 16:36:59 -0500340 }
341
342 /**
Julia Reynoldsef37f282016-02-12 09:11:27 -0500343 * Sets visibility override.
Julia Reynolds92d456e2016-01-25 16:36:59 -0500344 */
345 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -0500346 public void setVisibilityOverride(String pkgName, int uid, int visibility) {
347 getOrCreateRecord(pkgName, uid).visibility = visibility;
Julia Reynolds92d456e2016-01-25 16:36:59 -0500348 updateConfig();
349 }
350
351 /**
Julia Reynoldsef37f282016-02-12 09:11:27 -0500352 * Gets importance.
Julia Reynolds81afbcd2016-02-09 14:54:08 -0500353 */
354 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -0500355 public int getImportance(String packageName, int uid) {
356 return getOrCreateRecord(packageName, uid).importance;
Julia Reynolds81afbcd2016-02-09 14:54:08 -0500357 }
358
359 /**
Julia Reynoldsef37f282016-02-12 09:11:27 -0500360 * Sets importance.
Julia Reynolds92d456e2016-01-25 16:36:59 -0500361 */
362 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -0500363 public void setImportance(String pkgName, int uid, int importance) {
364 getOrCreateRecord(pkgName, uid).importance = importance;
Julia Reynoldsa07af882015-12-17 08:32:48 -0500365 updateConfig();
366 }
367
Chris Wrenacf424a2016-03-15 12:48:55 -0400368 public void setEnabled(String packageName, int uid, boolean enabled) {
369 boolean wasEnabled = getImportance(packageName, uid) != Ranking.IMPORTANCE_NONE;
370 if (wasEnabled == enabled) {
371 return;
372 }
373 setImportance(packageName, uid, enabled ? DEFAULT_IMPORTANCE : Ranking.IMPORTANCE_NONE);
374 }
375
Chris Wren54bbef42014-07-09 18:37:56 -0400376 public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
377 if (filter == null) {
378 final int N = mSignalExtractors.length;
379 pw.print(prefix);
380 pw.print("mSignalExtractors.length = ");
381 pw.println(N);
382 for (int i = 0; i < N; i++) {
383 pw.print(prefix);
384 pw.print(" ");
385 pw.println(mSignalExtractors[i]);
386 }
387 }
Chris Wren54bbef42014-07-09 18:37:56 -0400388 if (filter == null) {
389 pw.print(prefix);
John Spurlock1d881a12015-03-18 19:21:54 -0400390 pw.println("per-package config:");
Chris Wren54bbef42014-07-09 18:37:56 -0400391 }
Julia Reynolds85aa6cb2016-01-08 17:49:11 -0500392 pw.println("Records:");
John Spurlock35ef0a62015-05-28 11:24:10 -0400393 dumpRecords(pw, prefix, filter, mRecords);
Julia Reynolds85aa6cb2016-01-08 17:49:11 -0500394 pw.println("Restored without uid:");
John Spurlock35ef0a62015-05-28 11:24:10 -0400395 dumpRecords(pw, prefix, filter, mRestoredWithoutUids);
396 }
397
398 private static void dumpRecords(PrintWriter pw, String prefix,
399 NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) {
400 final int N = records.size();
Chris Wren54bbef42014-07-09 18:37:56 -0400401 for (int i = 0; i < N; i++) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400402 final Record r = records.valueAt(i);
John Spurlock1d881a12015-03-18 19:21:54 -0400403 if (filter == null || filter.matches(r.pkg)) {
404 pw.print(prefix);
405 pw.print(" ");
406 pw.print(r.pkg);
407 pw.print(" (");
John Spurlock35ef0a62015-05-28 11:24:10 -0400408 pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
John Spurlock1d881a12015-03-18 19:21:54 -0400409 pw.print(')');
Julia Reynolds92d456e2016-01-25 16:36:59 -0500410 if (r.importance != DEFAULT_IMPORTANCE) {
411 pw.print(" importance=");
412 pw.print(Ranking.importanceToString(r.importance));
413 }
414 if (r.priority != DEFAULT_PRIORITY) {
415 pw.print(" priority=");
Chris Wrenacf424a2016-03-15 12:48:55 -0400416 pw.print(Notification.priorityToString(r.priority));
Julia Reynolds92d456e2016-01-25 16:36:59 -0500417 }
418 if (r.visibility != DEFAULT_VISIBILITY) {
419 pw.print(" visibility=");
Chris Wrenacf424a2016-03-15 12:48:55 -0400420 pw.print(Notification.visibilityToString(r.visibility));
Julia Reynolds92d456e2016-01-25 16:36:59 -0500421 }
John Spurlock1d881a12015-03-18 19:21:54 -0400422 pw.println();
Chris Wren54bbef42014-07-09 18:37:56 -0400423 }
424 }
425 }
John Spurlock1d881a12015-03-18 19:21:54 -0400426
Chris Wrenacf424a2016-03-15 12:48:55 -0400427 public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
428 JSONObject ranking = new JSONObject();
429 JSONArray records = new JSONArray();
430 try {
431 ranking.put("noUid", mRestoredWithoutUids.size());
432 } catch (JSONException e) {
433 // pass
434 }
435 final int N = mRecords.size();
436 for (int i = 0; i < N; i++) {
437 final Record r = mRecords.valueAt(i);
438 if (filter == null || filter.matches(r.pkg)) {
439 JSONObject record = new JSONObject();
440 try {
441 record.put("userId", UserHandle.getUserId(r.uid));
442 record.put("packageName", r.pkg);
443 if (r.importance != DEFAULT_IMPORTANCE) {
444 record.put("importance", Ranking.importanceToString(r.importance));
445 }
446 if (r.priority != DEFAULT_PRIORITY) {
447 record.put("priority", Notification.priorityToString(r.priority));
448 }
449 if (r.visibility != DEFAULT_VISIBILITY) {
450 record.put("visibility", Notification.visibilityToString(r.visibility));
451 }
452 } catch (JSONException e) {
453 // pass
454 }
455 records.put(record);
456 }
457 }
458 try {
459 ranking.put("records", records);
460 } catch (JSONException e) {
461 // pass
462 }
463 return ranking;
464 }
465
466 /**
467 * Dump only the ban information as structured JSON for the stats collector.
468 *
469 * This is intentionally redundant with {#link dumpJson} because the old
470 * scraper will expect this format.
471 *
472 * @param filter
473 * @return
474 */
475 public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
476 JSONArray bans = new JSONArray();
477 Map<Integer, String> packageBans = getPackageBans();
478 for(Entry<Integer, String> ban : packageBans.entrySet()) {
479 final int userId = UserHandle.getUserId(ban.getKey());
480 final String packageName = ban.getValue();
481 if (filter == null || filter.matches(packageName)) {
482 JSONObject banJson = new JSONObject();
483 try {
484 banJson.put("userId", userId);
485 banJson.put("packageName", packageName);
486 } catch (JSONException e) {
487 e.printStackTrace();
488 }
489 bans.put(banJson);
490 }
491 }
492 return bans;
493 }
494
495 public Map<Integer, String> getPackageBans() {
496 final int N = mRecords.size();
497 ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
498 for (int i = 0; i < N; i++) {
499 final Record r = mRecords.valueAt(i);
500 if (r.importance == Ranking.IMPORTANCE_NONE) {
501 packageBans.put(r.uid, r.pkg);
502 }
503 }
504 return packageBans;
505 }
506
Julia Reynolds6434eb22016-08-08 17:19:26 -0400507 public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
508 if (!removingPackage || pkgList == null || pkgList.length == 0
John Spurlock35ef0a62015-05-28 11:24:10 -0400509 || mRestoredWithoutUids.isEmpty()) {
510 return; // nothing to do
511 }
512 final PackageManager pm = mContext.getPackageManager();
513 boolean updated = false;
514 for (String pkg : pkgList) {
515 final Record r = mRestoredWithoutUids.get(pkg);
516 if (r != null) {
517 try {
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700518 //TODO: http://b/22388012
Jeff Sharkeye06b4d12016-01-06 14:51:50 -0700519 r.uid = pm.getPackageUidAsUser(r.pkg, UserHandle.USER_SYSTEM);
John Spurlock35ef0a62015-05-28 11:24:10 -0400520 mRestoredWithoutUids.remove(pkg);
521 mRecords.put(recordKey(r.pkg, r.uid), r);
522 updated = true;
523 } catch (NameNotFoundException e) {
524 // noop
525 }
526 }
527 }
528 if (updated) {
529 updateConfig();
530 }
531 }
532
John Spurlock1d881a12015-03-18 19:21:54 -0400533 private static class Record {
John Spurlock35ef0a62015-05-28 11:24:10 -0400534 static int UNKNOWN_UID = UserHandle.USER_NULL;
535
John Spurlock1d881a12015-03-18 19:21:54 -0400536 String pkg;
John Spurlock35ef0a62015-05-28 11:24:10 -0400537 int uid = UNKNOWN_UID;
Julia Reynoldsa07af882015-12-17 08:32:48 -0500538 int importance = DEFAULT_IMPORTANCE;
Julia Reynolds92d456e2016-01-25 16:36:59 -0500539 int priority = DEFAULT_PRIORITY;
540 int visibility = DEFAULT_VISIBILITY;
Julia Reynolds233a5f92015-10-19 13:51:23 -0400541 }
Chris Wren54bbef42014-07-09 18:37:56 -0400542}