Merge "track and report packages with undecorated remoteviews" into qt-qpr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
1eb5828022
@@ -325,7 +325,7 @@ message Atom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pulled events will start at field 10000.
|
// Pulled events will start at field 10000.
|
||||||
// Next: 10062
|
// Next: 10067
|
||||||
oneof pulled {
|
oneof pulled {
|
||||||
WifiBytesTransfer wifi_bytes_transfer = 10000;
|
WifiBytesTransfer wifi_bytes_transfer = 10000;
|
||||||
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
|
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
|
||||||
@@ -390,6 +390,7 @@ message Atom {
|
|||||||
AppOps app_ops = 10060;
|
AppOps app_ops = 10060;
|
||||||
ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
|
ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
|
||||||
VmsClientStats vms_client_stats = 10065;
|
VmsClientStats vms_client_stats = 10065;
|
||||||
|
NotificationRemoteViews notification_remote_views = 10066;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DO NOT USE field numbers above 100,000 in AOSP.
|
// DO NOT USE field numbers above 100,000 in AOSP.
|
||||||
@@ -4751,6 +4752,24 @@ message ProcStatsPkgProc {
|
|||||||
optional ProcessStatsSectionProto proc_stats_section = 1;
|
optional ProcessStatsSectionProto proc_stats_section = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Next Tag: 2
|
||||||
|
message PackageRemoteViewInfoProto {
|
||||||
|
optional string package_name = 1;
|
||||||
|
// add per-package additional info here (like channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next Tag: 2
|
||||||
|
message NotificationRemoteViewsProto {
|
||||||
|
repeated PackageRemoteViewInfoProto package_remote_view_info = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pulled from NotificationManagerService.java
|
||||||
|
*/
|
||||||
|
message NotificationRemoteViews {
|
||||||
|
optional NotificationRemoteViewsProto notification_remote_views = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message PowerProfileProto {
|
message PowerProfileProto {
|
||||||
optional double cpu_suspend = 1;
|
optional double cpu_suspend = 1;
|
||||||
|
|
||||||
|
|||||||
@@ -271,6 +271,9 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
|
|||||||
{android::util::VMS_CLIENT_STATS,
|
{android::util::VMS_CLIENT_STATS,
|
||||||
{.additiveFields = {5, 6, 7, 8, 9, 10},
|
{.additiveFields = {5, 6, 7, 8, 9, 10},
|
||||||
.puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}},
|
.puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}},
|
||||||
|
// NotiifcationRemoteViews.
|
||||||
|
{android::util::NOTIFICATION_REMOTE_VIEWS,
|
||||||
|
{.puller = new StatsCompanionServicePuller(android::util::NOTIFICATION_REMOTE_VIEWS)}},
|
||||||
};
|
};
|
||||||
|
|
||||||
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
|
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
|
||||||
|
|||||||
@@ -202,4 +202,6 @@ interface INotificationManager
|
|||||||
|
|
||||||
void setPrivateNotificationsAllowed(boolean allow);
|
void setPrivateNotificationsAllowed(boolean allow);
|
||||||
boolean getPrivateNotificationsAllowed();
|
boolean getPrivateNotificationsAllowed();
|
||||||
|
|
||||||
|
long pullStats(long startNs, int report, boolean doAgg, out List<ParcelFileDescriptor> stats);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -264,3 +264,14 @@ message ZenPolicyProto {
|
|||||||
optional Sender priority_calls = 16;
|
optional Sender priority_calls = 16;
|
||||||
optional Sender priority_messages = 17;
|
optional Sender priority_messages = 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Next Tag: 2
|
||||||
|
message PackageRemoteViewInfoProto {
|
||||||
|
optional string package_name = 1;
|
||||||
|
// add per-package additional info here (like channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next Tag: 2
|
||||||
|
message NotificationRemoteViewsProto {
|
||||||
|
repeated PackageRemoteViewInfoProto package_remote_view_info = 1;
|
||||||
|
}
|
||||||
@@ -163,6 +163,7 @@ import android.os.IDeviceIdleController;
|
|||||||
import android.os.IInterface;
|
import android.os.IInterface;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
@@ -281,6 +282,9 @@ public class NotificationManagerService extends SystemService {
|
|||||||
public static final boolean ENABLE_CHILD_NOTIFICATIONS
|
public static final boolean ENABLE_CHILD_NOTIFICATIONS
|
||||||
= SystemProperties.getBoolean("debug.child_notifs", true);
|
= SystemProperties.getBoolean("debug.child_notifs", true);
|
||||||
|
|
||||||
|
// pullStats report request: undecorated remote view stats
|
||||||
|
public static final int REPORT_REMOTE_VIEWS = 0x01;
|
||||||
|
|
||||||
static final boolean DEBUG_INTERRUPTIVENESS = SystemProperties.getBoolean(
|
static final boolean DEBUG_INTERRUPTIVENESS = SystemProperties.getBoolean(
|
||||||
"debug.notification.interruptiveness", false);
|
"debug.notification.interruptiveness", false);
|
||||||
|
|
||||||
@@ -3734,6 +3738,8 @@ public class NotificationManagerService extends SystemService {
|
|||||||
try {
|
try {
|
||||||
if (filter.stats) {
|
if (filter.stats) {
|
||||||
dumpJson(pw, filter);
|
dumpJson(pw, filter);
|
||||||
|
} else if (filter.rvStats) {
|
||||||
|
dumpRemoteViewStats(pw, filter);
|
||||||
} else if (filter.proto) {
|
} else if (filter.proto) {
|
||||||
dumpProto(fd, filter);
|
dumpProto(fd, filter);
|
||||||
} else if (filter.criticalPriority) {
|
} else if (filter.criticalPriority) {
|
||||||
@@ -4210,6 +4216,49 @@ public class NotificationManagerService extends SystemService {
|
|||||||
new NotificationShellCmd(NotificationManagerService.this)
|
new NotificationShellCmd(NotificationManagerService.this)
|
||||||
.exec(this, in, out, err, args, callback, resultReceiver);
|
.exec(this, in, out, err, args, callback, resultReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get stats committed after startNs
|
||||||
|
*
|
||||||
|
* @param startNs Report stats committed after this time in nanoseconds.
|
||||||
|
* @param report Indicatess which section to include in the stats.
|
||||||
|
* @param doAgg Whether to aggregate the stats or keep them separated.
|
||||||
|
* @param out List of protos of individual commits or one representing the
|
||||||
|
* aggregate.
|
||||||
|
* @return the report time in nanoseconds, or 0 on error.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long pullStats(long startNs, int report, boolean doAgg,
|
||||||
|
List<ParcelFileDescriptor> out) {
|
||||||
|
checkCallerIsSystemOrShell();
|
||||||
|
long startMs = TimeUnit.MILLISECONDS.convert(startNs, TimeUnit.NANOSECONDS);
|
||||||
|
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
switch (report) {
|
||||||
|
case REPORT_REMOTE_VIEWS:
|
||||||
|
Slog.e(TAG, "pullStats REPORT_REMOTE_VIEWS from: "
|
||||||
|
+ startMs + " wtih " + doAgg);
|
||||||
|
PulledStats stats = mUsageStats.remoteViewStats(startMs, doAgg);
|
||||||
|
if (stats != null) {
|
||||||
|
out.add(stats.toParcelFileDescriptor(report));
|
||||||
|
Slog.e(TAG, "exiting pullStats with: " + out.size());
|
||||||
|
long endNs = TimeUnit.NANOSECONDS
|
||||||
|
.convert(stats.endTimeMs(), TimeUnit.MILLISECONDS);
|
||||||
|
return endNs;
|
||||||
|
}
|
||||||
|
Slog.e(TAG, "null stats for: " + report);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
|
||||||
|
Slog.e(TAG, "exiting pullStats: on error", e);
|
||||||
|
return 0;
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
Slog.e(TAG, "exiting pullStats: bad request");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -4425,6 +4474,15 @@ public class NotificationManagerService extends SystemService {
|
|||||||
pw.println(dump);
|
pw.println(dump);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void dumpRemoteViewStats(PrintWriter pw, @NonNull DumpFilter filter) {
|
||||||
|
PulledStats stats = mUsageStats.remoteViewStats(filter.since, true);
|
||||||
|
if (stats == null) {
|
||||||
|
pw.println("no remote view stats reported.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stats.dump(REPORT_REMOTE_VIEWS, pw, filter);
|
||||||
|
}
|
||||||
|
|
||||||
private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter) {
|
private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter) {
|
||||||
final ProtoOutputStream proto = new ProtoOutputStream(fd);
|
final ProtoOutputStream proto = new ProtoOutputStream(fd);
|
||||||
synchronized (mNotificationLock) {
|
synchronized (mNotificationLock) {
|
||||||
@@ -8559,6 +8617,7 @@ public class NotificationManagerService extends SystemService {
|
|||||||
public boolean zen;
|
public boolean zen;
|
||||||
public long since;
|
public long since;
|
||||||
public boolean stats;
|
public boolean stats;
|
||||||
|
public boolean rvStats;
|
||||||
public boolean redact = true;
|
public boolean redact = true;
|
||||||
public boolean proto = false;
|
public boolean proto = false;
|
||||||
public boolean criticalPriority = false;
|
public boolean criticalPriority = false;
|
||||||
@@ -8594,6 +8653,14 @@ public class NotificationManagerService extends SystemService {
|
|||||||
} else {
|
} else {
|
||||||
filter.since = 0;
|
filter.since = 0;
|
||||||
}
|
}
|
||||||
|
} else if ("--remote-view-stats".equals(a)) {
|
||||||
|
filter.rvStats = true;
|
||||||
|
if (ai < args.length-1) {
|
||||||
|
ai++;
|
||||||
|
filter.since = Long.parseLong(args[ai]);
|
||||||
|
} else {
|
||||||
|
filter.since = 0;
|
||||||
|
}
|
||||||
} else if (PRIORITY_ARG.equals(a)) {
|
} else if (PRIORITY_ARG.equals(a)) {
|
||||||
// Bugreport will call the service twice with priority arguments, first to dump
|
// Bugreport will call the service twice with priority arguments, first to dump
|
||||||
// critical sections and then non critical ones. Set approriate filters
|
// critical sections and then non critical ones. Set approriate filters
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ public class NotificationUsageStats {
|
|||||||
stats.numPostedByApp++;
|
stats.numPostedByApp++;
|
||||||
stats.updateInterarrivalEstimate(now);
|
stats.updateInterarrivalEstimate(now);
|
||||||
stats.countApiUse(notification);
|
stats.countApiUse(notification);
|
||||||
|
stats.numUndecoratedRemoteViews += (isUndecoratedRemoteView(notification) ? 1 : 0);
|
||||||
}
|
}
|
||||||
releaseAggregatedStatsLocked(aggregatedStatsArray);
|
releaseAggregatedStatsLocked(aggregatedStatsArray);
|
||||||
if (ENABLE_SQLITE_LOG) {
|
if (ENABLE_SQLITE_LOG) {
|
||||||
@@ -156,6 +157,13 @@ public class NotificationUsageStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this notification use RemoveViews without a platform decoration?
|
||||||
|
*/
|
||||||
|
protected static boolean isUndecoratedRemoteView(NotificationRecord notification) {
|
||||||
|
return (notification.getNotification().getNotificationStyle() == null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a notification has been updated.
|
* Called when a notification has been updated.
|
||||||
*/
|
*/
|
||||||
@@ -326,6 +334,15 @@ public class NotificationUsageStats {
|
|||||||
return dump;
|
return dump;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PulledStats remoteViewStats(long startMs, boolean aggregate) {
|
||||||
|
if (ENABLE_SQLITE_LOG) {
|
||||||
|
if (aggregate) {
|
||||||
|
return mSQLiteLog.remoteViewAggStats(startMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void dump(PrintWriter pw, String indent, DumpFilter filter) {
|
public synchronized void dump(PrintWriter pw, String indent, DumpFilter filter) {
|
||||||
if (ENABLE_AGGREGATED_IN_MEMORY_STATS) {
|
if (ENABLE_AGGREGATED_IN_MEMORY_STATS) {
|
||||||
for (AggregatedStats as : mStats.values()) {
|
for (AggregatedStats as : mStats.values()) {
|
||||||
@@ -403,6 +420,7 @@ public class NotificationUsageStats {
|
|||||||
public int numRateViolations;
|
public int numRateViolations;
|
||||||
public int numAlertViolations;
|
public int numAlertViolations;
|
||||||
public int numQuotaViolations;
|
public int numQuotaViolations;
|
||||||
|
public int numUndecoratedRemoteViews;
|
||||||
public long mLastAccessTime;
|
public long mLastAccessTime;
|
||||||
|
|
||||||
public AggregatedStats(Context context, String key) {
|
public AggregatedStats(Context context, String key) {
|
||||||
@@ -669,6 +687,8 @@ public class NotificationUsageStats {
|
|||||||
output.append(indentPlusTwo).append(noisyImportance.toString()).append("\n");
|
output.append(indentPlusTwo).append(noisyImportance.toString()).append("\n");
|
||||||
output.append(indentPlusTwo).append(quietImportance.toString()).append("\n");
|
output.append(indentPlusTwo).append(quietImportance.toString()).append("\n");
|
||||||
output.append(indentPlusTwo).append(finalImportance.toString()).append("\n");
|
output.append(indentPlusTwo).append(finalImportance.toString()).append("\n");
|
||||||
|
output.append(indentPlusTwo);
|
||||||
|
output.append("numUndecorateRVs=").append(numUndecoratedRemoteViews).append("\n");
|
||||||
output.append(indent).append("}");
|
output.append(indent).append("}");
|
||||||
return output.toString();
|
return output.toString();
|
||||||
}
|
}
|
||||||
@@ -1027,7 +1047,7 @@ public class NotificationUsageStats {
|
|||||||
private static final int MSG_DISMISS = 4;
|
private static final int MSG_DISMISS = 4;
|
||||||
|
|
||||||
private static final String DB_NAME = "notification_log.db";
|
private static final String DB_NAME = "notification_log.db";
|
||||||
private static final int DB_VERSION = 5;
|
private static final int DB_VERSION = 7;
|
||||||
|
|
||||||
/** Age in ms after which events are pruned from the DB. */
|
/** Age in ms after which events are pruned from the DB. */
|
||||||
private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L; // 1 week
|
private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L; // 1 week
|
||||||
@@ -1060,6 +1080,7 @@ public class NotificationUsageStats {
|
|||||||
private static final String COL_FIRST_EXPANSIONTIME_MS = "first_expansion_time_ms";
|
private static final String COL_FIRST_EXPANSIONTIME_MS = "first_expansion_time_ms";
|
||||||
private static final String COL_AIRTIME_EXPANDED_MS = "expansion_airtime_ms";
|
private static final String COL_AIRTIME_EXPANDED_MS = "expansion_airtime_ms";
|
||||||
private static final String COL_EXPAND_COUNT = "expansion_count";
|
private static final String COL_EXPAND_COUNT = "expansion_count";
|
||||||
|
private static final String COL_UNDECORATED = "undecorated";
|
||||||
|
|
||||||
|
|
||||||
private static final int EVENT_TYPE_POST = 1;
|
private static final int EVENT_TYPE_POST = 1;
|
||||||
@@ -1085,12 +1106,20 @@ public class NotificationUsageStats {
|
|||||||
"COUNT(*) AS cnt, " +
|
"COUNT(*) AS cnt, " +
|
||||||
"SUM(" + COL_MUTED + ") as muted, " +
|
"SUM(" + COL_MUTED + ") as muted, " +
|
||||||
"SUM(" + COL_NOISY + ") as noisy, " +
|
"SUM(" + COL_NOISY + ") as noisy, " +
|
||||||
"SUM(" + COL_DEMOTED + ") as demoted " +
|
"SUM(" + COL_DEMOTED + ") as demoted, " +
|
||||||
|
"SUM(" + COL_UNDECORATED + ") as undecorated " +
|
||||||
"FROM " + TAB_LOG + " " +
|
"FROM " + TAB_LOG + " " +
|
||||||
"WHERE " +
|
"WHERE " +
|
||||||
COL_EVENT_TYPE + "=" + EVENT_TYPE_POST +
|
COL_EVENT_TYPE + "=" + EVENT_TYPE_POST +
|
||||||
" AND " + COL_EVENT_TIME + " > %d " +
|
" AND " + COL_EVENT_TIME + " > %d " +
|
||||||
" GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
|
" GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
|
||||||
|
private static final String UNDECORATED_QUERY = "SELECT " +
|
||||||
|
COL_PKG + ", " +
|
||||||
|
"MAX(" + COL_EVENT_TIME + ") as max_time " +
|
||||||
|
"FROM " + TAB_LOG + " " +
|
||||||
|
"WHERE " + COL_UNDECORATED + "> 0 " +
|
||||||
|
" AND " + COL_EVENT_TIME + " > %d " +
|
||||||
|
"GROUP BY " + COL_PKG;
|
||||||
|
|
||||||
public SQLiteLog(Context context) {
|
public SQLiteLog(Context context) {
|
||||||
HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log",
|
HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log",
|
||||||
@@ -1146,7 +1175,8 @@ public class NotificationUsageStats {
|
|||||||
COL_AIRTIME_MS + " INT," +
|
COL_AIRTIME_MS + " INT," +
|
||||||
COL_FIRST_EXPANSIONTIME_MS + " INT," +
|
COL_FIRST_EXPANSIONTIME_MS + " INT," +
|
||||||
COL_AIRTIME_EXPANDED_MS + " INT," +
|
COL_AIRTIME_EXPANDED_MS + " INT," +
|
||||||
COL_EXPAND_COUNT + " INT" +
|
COL_EXPAND_COUNT + " INT," +
|
||||||
|
COL_UNDECORATED + " INT" +
|
||||||
")");
|
")");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1256,6 +1286,7 @@ public class NotificationUsageStats {
|
|||||||
} else {
|
} else {
|
||||||
putPosttimeVisibility(r, cv);
|
putPosttimeVisibility(r, cv);
|
||||||
}
|
}
|
||||||
|
cv.put(COL_UNDECORATED, (isUndecoratedRemoteView(r) ? 1 : 0));
|
||||||
SQLiteDatabase db = mHelper.getWritableDatabase();
|
SQLiteDatabase db = mHelper.getWritableDatabase();
|
||||||
if (db.insert(TAB_LOG, null, cv) < 0) {
|
if (db.insert(TAB_LOG, null, cv) < 0) {
|
||||||
Log.wtf(TAG, "Error while trying to insert values: " + cv);
|
Log.wtf(TAG, "Error while trying to insert values: " + cv);
|
||||||
@@ -1332,5 +1363,22 @@ public class NotificationUsageStats {
|
|||||||
}
|
}
|
||||||
return dump;
|
return dump;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PulledStats remoteViewAggStats(long startMs) {
|
||||||
|
PulledStats stats = new PulledStats(startMs);
|
||||||
|
SQLiteDatabase db = mHelper.getReadableDatabase();
|
||||||
|
String q = String.format(UNDECORATED_QUERY, startMs);
|
||||||
|
Cursor cursor = db.rawQuery(q, null);
|
||||||
|
try {
|
||||||
|
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||||||
|
String pkg = cursor.getString(0);
|
||||||
|
long maxTimeMs = cursor.getLong(1);
|
||||||
|
stats.addUndecoratedPackage(pkg, maxTimeMs);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.notification;
|
||||||
|
|
||||||
|
import static com.android.server.notification.NotificationManagerService.REPORT_REMOTE_VIEWS;
|
||||||
|
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.service.notification.NotificationRemoteViewsProto;
|
||||||
|
import android.service.notification.PackageRemoteViewInfoProto;
|
||||||
|
import android.util.Slog;
|
||||||
|
import android.util.proto.ProtoOutputStream;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PulledStats {
|
||||||
|
static final String TAG = "PulledStats";
|
||||||
|
|
||||||
|
private final long mTimePeriodStartMs;
|
||||||
|
private long mTimePeriodEndMs;
|
||||||
|
private List<String> mUndecoratedPackageNames;
|
||||||
|
|
||||||
|
public PulledStats(long startMs) {
|
||||||
|
mTimePeriodEndMs = mTimePeriodStartMs = startMs;
|
||||||
|
mUndecoratedPackageNames = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ParcelFileDescriptor toParcelFileDescriptor(int report)
|
||||||
|
throws IOException {
|
||||||
|
final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
|
||||||
|
switch(report) {
|
||||||
|
case REPORT_REMOTE_VIEWS:
|
||||||
|
Thread thr = new Thread("NotificationManager pulled metric output") {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(
|
||||||
|
fds[1]);
|
||||||
|
final ProtoOutputStream proto = new ProtoOutputStream(fout);
|
||||||
|
writeToProto(report, proto);
|
||||||
|
proto.flush();
|
||||||
|
fout.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.w(TAG, "Failure writing pipe", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
thr.start();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Slog.w(TAG, "Unknown pulled stats request: " + report);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return fds[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @return the most recent timestamp in the report, as nanoseconds.
|
||||||
|
*/
|
||||||
|
public long endTimeMs() {
|
||||||
|
return mTimePeriodEndMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dump(int report, PrintWriter pw, NotificationManagerService.DumpFilter filter) {
|
||||||
|
switch(report) {
|
||||||
|
case REPORT_REMOTE_VIEWS:
|
||||||
|
pw.print(" Packages with undecordated notifications (");
|
||||||
|
pw.print(mTimePeriodStartMs);
|
||||||
|
pw.print(" - ");
|
||||||
|
pw.print(mTimePeriodEndMs);
|
||||||
|
pw.println("):");
|
||||||
|
if (mUndecoratedPackageNames.size() == 0) {
|
||||||
|
pw.println(" none");
|
||||||
|
} else {
|
||||||
|
for (String pkg : mUndecoratedPackageNames) {
|
||||||
|
if (!filter.filtered || pkg.equals(filter.pkgFilter)) {
|
||||||
|
pw.println(" " + pkg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pw.println("Unknown pulled stats request: " + report);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeToProto(int report, ProtoOutputStream proto) {
|
||||||
|
switch(report) {
|
||||||
|
case REPORT_REMOTE_VIEWS:
|
||||||
|
for (String pkg: mUndecoratedPackageNames) {
|
||||||
|
long token = proto.start(NotificationRemoteViewsProto.PACKAGE_REMOTE_VIEW_INFO);
|
||||||
|
proto.write(PackageRemoteViewInfoProto.PACKAGE_NAME, pkg);
|
||||||
|
proto.end(token);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Slog.w(TAG, "Unknown pulled stats request: " + report);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addUndecoratedPackage(String packageName, long timestampMs) {
|
||||||
|
mUndecoratedPackageNames.add(packageName);
|
||||||
|
mTimePeriodEndMs = Math.max(mTimePeriodEndMs, timestampMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,6 +41,7 @@ import android.app.AppOpsManager.HistoricalOps;
|
|||||||
import android.app.AppOpsManager.HistoricalOpsRequest;
|
import android.app.AppOpsManager.HistoricalOpsRequest;
|
||||||
import android.app.AppOpsManager.HistoricalPackageOps;
|
import android.app.AppOpsManager.HistoricalPackageOps;
|
||||||
import android.app.AppOpsManager.HistoricalUidOps;
|
import android.app.AppOpsManager.HistoricalUidOps;
|
||||||
|
import android.app.INotificationManager;
|
||||||
import android.app.ProcessMemoryState;
|
import android.app.ProcessMemoryState;
|
||||||
import android.app.StatsManager;
|
import android.app.StatsManager;
|
||||||
import android.bluetooth.BluetoothActivityEnergyInfo;
|
import android.bluetooth.BluetoothActivityEnergyInfo;
|
||||||
@@ -140,6 +141,7 @@ import com.android.server.SystemService;
|
|||||||
import com.android.server.SystemServiceManager;
|
import com.android.server.SystemServiceManager;
|
||||||
import com.android.server.am.MemoryStatUtil.IonAllocations;
|
import com.android.server.am.MemoryStatUtil.IonAllocations;
|
||||||
import com.android.server.am.MemoryStatUtil.MemoryStat;
|
import com.android.server.am.MemoryStatUtil.MemoryStat;
|
||||||
|
import com.android.server.notification.NotificationManagerService;
|
||||||
import com.android.server.role.RoleManagerInternal;
|
import com.android.server.role.RoleManagerInternal;
|
||||||
import com.android.server.storage.DiskStatsFileLogger;
|
import com.android.server.storage.DiskStatsFileLogger;
|
||||||
import com.android.server.storage.DiskStatsLoggingService;
|
import com.android.server.storage.DiskStatsLoggingService;
|
||||||
@@ -1625,14 +1627,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
|||||||
if (statsFiles.size() != 1) {
|
if (statsFiles.size() != 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(
|
unpackStreamedData(tagId, elapsedNanos, wallClockNanos, pulledData, statsFiles);
|
||||||
statsFiles.get(0));
|
|
||||||
int[] len = new int[1];
|
|
||||||
byte[] stats = readFully(stream, len);
|
|
||||||
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
|
|
||||||
wallClockNanos);
|
|
||||||
e.writeStorage(Arrays.copyOf(stats, len[0]));
|
|
||||||
pulledData.add(e);
|
|
||||||
new File(mBaseDir.getAbsolutePath() + "/" + section + "_"
|
new File(mBaseDir.getAbsolutePath() + "/" + section + "_"
|
||||||
+ lastHighWaterMark).delete();
|
+ lastHighWaterMark).delete();
|
||||||
new File(
|
new File(
|
||||||
@@ -1648,6 +1643,52 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private INotificationManager mNotificationManager =
|
||||||
|
INotificationManager.Stub.asInterface(
|
||||||
|
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
||||||
|
|
||||||
|
private void pullNotificationStats(int reportId, int tagId, long elapsedNanos,
|
||||||
|
long wallClockNanos,
|
||||||
|
List<StatsLogEventWrapper> pulledData) {
|
||||||
|
final long callingToken = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
// determine last pull tine. Copy file trick from pullProcessStats?
|
||||||
|
long lastNotificationStatsNs = wallClockNanos -
|
||||||
|
TimeUnit.NANOSECONDS.convert(1, TimeUnit.DAYS);
|
||||||
|
|
||||||
|
List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
|
||||||
|
long notificationStatsNs = mNotificationManager.pullStats(
|
||||||
|
lastNotificationStatsNs, reportId, true, statsFiles);
|
||||||
|
if (statsFiles.size() != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unpackStreamedData(tagId, elapsedNanos, wallClockNanos, pulledData, statsFiles);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Getting notistats failed: ", e);
|
||||||
|
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Getting notistats failed: ", e);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
Log.e(TAG, "Getting notistats failed: ", e);
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(callingToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unpackStreamedData(int tagId, long elapsedNanos, long wallClockNanos,
|
||||||
|
List<StatsLogEventWrapper> pulledData, List<ParcelFileDescriptor> statsFiles)
|
||||||
|
throws IOException {
|
||||||
|
InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(
|
||||||
|
statsFiles.get(0));
|
||||||
|
int[] len = new int[1];
|
||||||
|
byte[] stats = readFully(stream, len);
|
||||||
|
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
|
||||||
|
wallClockNanos);
|
||||||
|
e.writeStorage(Arrays.copyOf(stats, len[0]));
|
||||||
|
pulledData.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
|
static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
final int initialAvail = stream.available();
|
final int initialAvail = stream.available();
|
||||||
@@ -2477,6 +2518,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
|||||||
pullAppOps(elapsedNanos, wallClockNanos, ret);
|
pullAppOps(elapsedNanos, wallClockNanos, ret);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case StatsLog.NOTIFICATION_REMOTE_VIEWS: {
|
||||||
|
pullNotificationStats(NotificationManagerService.REPORT_REMOTE_VIEWS,
|
||||||
|
tagId, elapsedNanos, wallClockNanos, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
Slog.w(TAG, "No such tagId data as " + tagId);
|
Slog.w(TAG, "No such tagId data as " + tagId);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ android_test {
|
|||||||
"androidx.test.rules", "hamcrest-library",
|
"androidx.test.rules", "hamcrest-library",
|
||||||
"mockito-target-inline-minus-junit4",
|
"mockito-target-inline-minus-junit4",
|
||||||
"platform-test-annotations",
|
"platform-test-annotations",
|
||||||
|
"platformprotosnano",
|
||||||
"hamcrest-library",
|
"hamcrest-library",
|
||||||
"testables",
|
"testables",
|
||||||
"truth-prebuilt",
|
"truth-prebuilt",
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License
|
||||||
|
*/
|
||||||
|
package com.android.server.notification;
|
||||||
|
|
||||||
|
import static com.android.server.notification.NotificationManagerService.REPORT_REMOTE_VIEWS;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
import static junit.framework.Assert.assertNotSame;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import android.service.notification.nano.NotificationRemoteViewsProto;
|
||||||
|
import android.test.MoreAsserts;
|
||||||
|
import android.util.proto.ProtoOutputStream;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.server.UiServiceTestCase;
|
||||||
|
|
||||||
|
import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
public class PulledStatsTest extends UiServiceTestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPulledStats_Empty() {
|
||||||
|
PulledStats stats = new PulledStats(0L);
|
||||||
|
assertEquals(0L, stats.endTimeMs());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPulledStats_UnknownReport() {
|
||||||
|
PulledStats stats = new PulledStats(0L);
|
||||||
|
stats.addUndecoratedPackage("foo", 456);
|
||||||
|
stats.addUndecoratedPackage("bar", 123);
|
||||||
|
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
final ProtoOutputStream proto = new ProtoOutputStream(bytes);
|
||||||
|
stats.writeToProto(1023123, proto); // a very large number
|
||||||
|
proto.flush();
|
||||||
|
|
||||||
|
// expect empty output in response to an unrecognized request
|
||||||
|
assertEquals(0L, bytes.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPulledStats_RemoteViewReportPackages() {
|
||||||
|
List<String> expectedPkgs = new ArrayList<>(2);
|
||||||
|
expectedPkgs.add("foo");
|
||||||
|
expectedPkgs.add("bar");
|
||||||
|
|
||||||
|
PulledStats stats = new PulledStats(0L);
|
||||||
|
for(String pkg: expectedPkgs) {
|
||||||
|
stats.addUndecoratedPackage(pkg, 111);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
final ProtoOutputStream protoStream = new ProtoOutputStream(bytes);
|
||||||
|
stats.writeToProto(REPORT_REMOTE_VIEWS, protoStream);
|
||||||
|
protoStream.flush();
|
||||||
|
|
||||||
|
try {
|
||||||
|
NotificationRemoteViewsProto proto =
|
||||||
|
NotificationRemoteViewsProto.parseFrom(bytes.toByteArray());
|
||||||
|
List<String> actualPkgs = new ArrayList<>(2);
|
||||||
|
for(int i = 0 ; i < proto.packageRemoteViewInfo.length; i++) {
|
||||||
|
actualPkgs.add(proto.packageRemoteViewInfo[i].packageName);
|
||||||
|
}
|
||||||
|
assertEquals(2, actualPkgs.size());
|
||||||
|
assertTrue("missing packages", actualPkgs.containsAll(expectedPkgs));
|
||||||
|
assertTrue("unexpected packages", expectedPkgs.containsAll(actualPkgs));
|
||||||
|
} catch (InvalidProtocolBufferNanoException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail("writeToProto generated unparsable output");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testPulledStats_RemoteViewReportEndTime() {
|
||||||
|
List<String> expectedPkgs = new ArrayList<>(2);
|
||||||
|
expectedPkgs.add("foo");
|
||||||
|
expectedPkgs.add("bar");
|
||||||
|
|
||||||
|
PulledStats stats = new PulledStats(0L);
|
||||||
|
long t = 111;
|
||||||
|
for(String pkg: expectedPkgs) {
|
||||||
|
t += 1000;
|
||||||
|
stats.addUndecoratedPackage(pkg, t);
|
||||||
|
}
|
||||||
|
assertEquals(t, stats.endTimeMs());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user