diff options
author | Chris Wren <cwren@android.com> | 2015-07-07 15:54:19 -0400 |
---|---|---|
committer | Chris Wren <cwren@android.com> | 2015-07-07 16:34:50 -0400 |
commit | e4b38805d62493c16003e501e7d32c534d465415 (patch) | |
tree | 2d242fc4b3c5d6f720990441793878af8bacee33 | |
parent | 937edac8e8583023019b625dd426ebf65169e30d (diff) | |
download | frameworks_base-e4b38805d62493c16003e501e7d32c534d465415.zip frameworks_base-e4b38805d62493c16003e501e7d32c534d465415.tar.gz frameworks_base-e4b38805d62493c16003e501e7d32c534d465415.tar.bz2 |
provide a cleaner notification dump
Respond to --stats by generating a cleaner, easier to parse dump.
Optional argument tells historical stats when to start aggregating.
Don't emit debug information about current notification status.
Bug: 20451514
Change-Id: Ie3d25b674421caa6c9e093f5643cb18d4138a7c8
-rw-r--r-- | services/core/java/com/android/server/notification/NotificationManagerService.java | 97 | ||||
-rw-r--r-- | services/core/java/com/android/server/notification/NotificationUsageStats.java | 135 |
2 files changed, 211 insertions, 21 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 87e9d42..b23b856 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -112,6 +112,9 @@ import com.android.server.statusbar.StatusBarManagerInternal; import libcore.io.IoUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -1663,7 +1666,12 @@ public class NotificationManagerService extends SystemService { return; } - dumpImpl(pw, DumpFilter.parseFromArguments(args)); + final DumpFilter filter = DumpFilter.parseFromArguments(args); + if (filter != null && filter.stats) { + dumpJson(pw, filter); + } else { + dumpImpl(pw, filter); + } } @Override @@ -1799,6 +1807,32 @@ public class NotificationManagerService extends SystemService { return "callState"; } return null; + }; + + private void dumpJson(PrintWriter pw, DumpFilter filter) { + JSONObject dump = new JSONObject(); + try { + dump.put("service", "Notification Manager"); + JSONArray bans = new JSONArray(); + try { + ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter); + for (Integer userId : packageBans.keySet()) { + for (String packageName : packageBans.get(userId)) { + JSONObject ban = new JSONObject(); + ban.put("userId", userId); + ban.put("packageName", packageName); + bans.put(ban); + } + } + } catch (NameNotFoundException e) { + // pass + } + dump.put("bans", bans); + dump.put("stats", mUsageStats.dumpJson(filter)); + } catch (JSONException e) { + e.printStackTrace(); + } + pw.println(dump); } void dumpImpl(PrintWriter pw, DumpFilter filter) { @@ -1920,18 +1954,10 @@ public class NotificationManagerService extends SystemService { try { pw.println("\n Banned Packages:"); - for(UserInfo user : UserManager.get(getContext()).getUsers()) { - final int userId = user.getUserHandle().getIdentifier(); - pw.println(" UserId " + userId); - final PackageManager packageManager = getContext().getPackageManager(); - List<PackageInfo> packages = packageManager.getInstalledPackages(0, userId); - final int packageCount = packages.size(); - for (int p = 0; p < packageCount; p++) { - final String packageName = packages.get(p).packageName; - final int uid = packageManager.getPackageUid(packageName, userId); - if (!checkNotificationOp(packageName, uid)) { - pw.println(" " + packageName); - } + ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter); + for (Integer userId : packageBans.keySet()) { + for (String packageName : packageBans.get(userId)) { + pw.println(" " + userId + ": " + packageName); } } } catch (NameNotFoundException e) { @@ -1940,6 +1966,32 @@ public class NotificationManagerService extends SystemService { } } + private ArrayMap<Integer, ArrayList<String>> getPackageBans(DumpFilter filter) + throws NameNotFoundException { + ArrayMap<Integer, ArrayList<String>> packageBans = new ArrayMap<>(); + ArrayList<String> packageNames = new ArrayList<>(); + for (UserInfo user : UserManager.get(getContext()).getUsers()) { + final int userId = user.getUserHandle().getIdentifier(); + final PackageManager packageManager = getContext().getPackageManager(); + List<PackageInfo> packages = packageManager.getInstalledPackages(0, userId); + final int packageCount = packages.size(); + for (int p = 0; p < packageCount; p++) { + final String packageName = packages.get(p).packageName; + if (filter == null || filter.matches(packageName)) { + final int uid = packageManager.getPackageUid(packageName, userId); + if (!checkNotificationOp(packageName, uid)) { + packageNames.add(packageName); + } + } + } + if (!packageNames.isEmpty()) { + packageBans.put(userId, packageNames); + packageNames = new ArrayList<>(); + } + } + return packageBans; + } + /** * The private API only accessible to the system process. */ @@ -3449,6 +3501,9 @@ public class NotificationManagerService extends SystemService { public static final class DumpFilter { public String pkgFilter; public boolean zen; + public long since; + public boolean stats; + private boolean all; public static DumpFilter parseFromArguments(String[] args) { if (args != null && args.length == 2 && "p".equals(args[0]) @@ -3460,27 +3515,35 @@ public class NotificationManagerService extends SystemService { if (args != null && args.length == 1 && "zen".equals(args[0])) { final DumpFilter filter = new DumpFilter(); filter.zen = true; + filter.all = true; + return filter; + } + if (args != null && args.length >= 1 && "--stats".equals(args[0])) { + final DumpFilter filter = new DumpFilter(); + filter.stats = true; + filter.since = args.length == 2 ? Long.valueOf(args[1]) : 0; + filter.all = true; return filter; } return null; } public boolean matches(StatusBarNotification sbn) { - return zen ? true : sbn != null + return all ? true : sbn != null && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg())); } public boolean matches(ComponentName component) { - return zen ? true : component != null && matches(component.getPackageName()); + return all ? true : component != null && matches(component.getPackageName()); } public boolean matches(String pkg) { - return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter); + return all ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter); } @Override public String toString() { - return zen ? "zen" : ('\'' + pkgFilter + '\''); + return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\''); } } diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index 2f0cc0f..0d6e8d6 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -22,7 +22,6 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; @@ -32,8 +31,14 @@ import android.util.Log; import com.android.internal.logging.MetricsLogger; import com.android.server.notification.NotificationManagerService.DumpFilter; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.PrintWriter; import java.util.ArrayDeque; +import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; @@ -220,6 +225,31 @@ public class NotificationUsageStats { return result; } + public synchronized JSONObject dumpJson(DumpFilter filter) { + JSONObject dump = new JSONObject(); + if (ENABLE_AGGREGATED_IN_MEMORY_STATS) { + try { + JSONArray aggregatedStats = new JSONArray(); + for (AggregatedStats as : mStats.values()) { + if (filter != null && !filter.matches(as.key)) + continue; + aggregatedStats.put(as.dumpJson()); + } + dump.put("current", aggregatedStats); + } catch (JSONException e) { + // pass + } + } + if (ENABLE_SQLITE_LOG) { + try { + dump.put("historical", mSQLiteLog.dumpJson(filter)); + } catch (JSONException e) { + // pass + } + } + return dump; + } + public synchronized void dump(PrintWriter pw, String indent, DumpFilter filter) { if (ENABLE_AGGREGATED_IN_MEMORY_STATS) { for (AggregatedStats as : mStats.values()) { @@ -250,6 +280,7 @@ public class NotificationUsageStats { private final Context mContext; public final String key; + private final long mCreated; private AggregatedStats mPrevious; // ---- Updated as the respective events occur. @@ -285,6 +316,7 @@ public class NotificationUsageStats { public AggregatedStats(Context context, String key) { this.key = key; mContext = context; + mCreated = SystemClock.elapsedRealtime(); } public void countApiUse(NotificationRecord record) { @@ -450,6 +482,47 @@ public class NotificationUsageStats { indent + " numBlocked=" + numBlocked + ",\n" + indent + "}"; } + + public JSONObject dumpJson() throws JSONException { + JSONObject dump = new JSONObject(); + dump.put("key", key); + dump.put("duration", SystemClock.elapsedRealtime() - mCreated); + maybePut(dump, "numPostedByApp", numPostedByApp); + maybePut(dump, "numUpdatedByApp", numUpdatedByApp); + maybePut(dump, "numRemovedByApp", numRemovedByApp); + maybePut(dump, "numPeopleCacheHit", numPeopleCacheHit); + maybePut(dump, "numPeopleCacheMiss", numPeopleCacheMiss); + maybePut(dump, "numWithStaredPeople", numWithStaredPeople); + maybePut(dump, "numWithValidPeople", numWithValidPeople); + maybePut(dump, "numBlocked", numBlocked); + maybePut(dump, "numWithActions", numWithActions); + maybePut(dump, "numPrivate", numPrivate); + maybePut(dump, "numSecret", numSecret); + maybePut(dump, "numPriorityMax", numPriorityMax); + maybePut(dump, "numPriorityHigh", numPriorityHigh); + maybePut(dump, "numPriorityLow", numPriorityLow); + maybePut(dump, "numPriorityMin", numPriorityMin); + maybePut(dump, "numInterrupt", numInterrupt); + maybePut(dump, "numWithBigText", numWithBigText); + maybePut(dump, "numWithBigPicture", numWithBigPicture); + maybePut(dump, "numForegroundService", numForegroundService); + maybePut(dump, "numOngoing", numOngoing); + maybePut(dump, "numAutoCancel", numAutoCancel); + maybePut(dump, "numWithLargeIcon", numWithLargeIcon); + maybePut(dump, "numWithInbox", numWithInbox); + maybePut(dump, "numWithMediaSession", numWithMediaSession); + maybePut(dump, "numWithTitle", numWithTitle); + maybePut(dump, "numWithText", numWithText); + maybePut(dump, "numWithSubText", numWithSubText); + maybePut(dump, "numWithInfoText", numWithInfoText); + return dump; + } + + private void maybePut(JSONObject dump, String name, int value) throws JSONException { + if (value > 0) { + dump.put(name, value); + } + } } /** @@ -780,14 +853,51 @@ public class NotificationUsageStats { mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_DISMISS, notification)); } + private JSONArray JsonPostFrequencies(DumpFilter filter) throws JSONException { + JSONArray frequencies = new JSONArray(); + SQLiteDatabase db = mHelper.getReadableDatabase(); + long midnight = getMidnightMs(); + String q = "SELECT " + + COL_EVENT_USER_ID + ", " + + COL_PKG + ", " + + // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)' + "CAST(((" + midnight + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " + + "AS day, " + + "COUNT(*) AS cnt " + + "FROM " + TAB_LOG + " " + + "WHERE " + + COL_EVENT_TYPE + "=" + EVENT_TYPE_POST + + " AND " + COL_EVENT_TIME + " > " + filter.since + + " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG; + Cursor cursor = db.rawQuery(q, null); + try { + for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { + int userId = cursor.getInt(0); + String pkg = cursor.getString(1); + if (filter != null && !filter.matches(pkg)) continue; + int day = cursor.getInt(2); + int count = cursor.getInt(3); + JSONObject row = new JSONObject(); + row.put("user_id", userId); + row.put("package", pkg); + row.put("day", day); + row.put("count", count); + frequencies.put(row); + } + } finally { + cursor.close(); + } + return frequencies; + } + public void printPostFrequencies(PrintWriter pw, String indent, DumpFilter filter) { SQLiteDatabase db = mHelper.getReadableDatabase(); - long nowMs = System.currentTimeMillis(); + long midnight = getMidnightMs(); String q = "SELECT " + COL_EVENT_USER_ID + ", " + COL_PKG + ", " + - // Bucket by day by looking at 'floor((nowMs - eventTimeMs) / dayMs)' - "CAST(((" + nowMs + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " + + // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)' + "CAST(((" + midnight + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " + "AS day, " + "COUNT(*) AS cnt " + "FROM " + TAB_LOG + " " + @@ -810,6 +920,13 @@ public class NotificationUsageStats { } } + private long getMidnightMs() { + GregorianCalendar midnight = new GregorianCalendar(); + midnight.set(midnight.get(Calendar.YEAR), midnight.get(Calendar.MONTH), + midnight.get(Calendar.DATE), 23, 59, 59); + return midnight.getTimeInMillis(); + } + private void writeEvent(long eventTimeMs, int eventType, NotificationRecord r) { ContentValues cv = new ContentValues(); cv.put(COL_EVENT_USER_ID, r.sbn.getUser().getIdentifier()); @@ -874,5 +991,15 @@ public class NotificationUsageStats { public void dump(PrintWriter pw, String indent, DumpFilter filter) { printPostFrequencies(pw, indent, filter); } + + public JSONObject dumpJson(DumpFilter filter) { + JSONObject dump = new JSONObject(); + try { + dump.put("post_frequency", JsonPostFrequencies(filter)); + } catch (JSONException e) { + // pass + } + return dump; + } } } |