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.
|
||||
// Next: 10062
|
||||
// Next: 10067
|
||||
oneof pulled {
|
||||
WifiBytesTransfer wifi_bytes_transfer = 10000;
|
||||
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
|
||||
@@ -390,6 +390,7 @@ message Atom {
|
||||
AppOps app_ops = 10060;
|
||||
ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
|
||||
VmsClientStats vms_client_stats = 10065;
|
||||
NotificationRemoteViews notification_remote_views = 10066;
|
||||
}
|
||||
|
||||
// DO NOT USE field numbers above 100,000 in AOSP.
|
||||
@@ -4751,6 +4752,24 @@ message ProcStatsPkgProc {
|
||||
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 {
|
||||
optional double cpu_suspend = 1;
|
||||
|
||||
|
||||
@@ -271,6 +271,9 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
|
||||
{android::util::VMS_CLIENT_STATS,
|
||||
{.additiveFields = {5, 6, 7, 8, 9, 10},
|
||||
.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) {
|
||||
|
||||
@@ -202,4 +202,6 @@ interface INotificationManager
|
||||
|
||||
void setPrivateNotificationsAllowed(boolean allow);
|
||||
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_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.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
@@ -281,6 +282,9 @@ public class NotificationManagerService extends SystemService {
|
||||
public static final boolean ENABLE_CHILD_NOTIFICATIONS
|
||||
= 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(
|
||||
"debug.notification.interruptiveness", false);
|
||||
|
||||
@@ -3734,6 +3738,8 @@ public class NotificationManagerService extends SystemService {
|
||||
try {
|
||||
if (filter.stats) {
|
||||
dumpJson(pw, filter);
|
||||
} else if (filter.rvStats) {
|
||||
dumpRemoteViewStats(pw, filter);
|
||||
} else if (filter.proto) {
|
||||
dumpProto(fd, filter);
|
||||
} else if (filter.criticalPriority) {
|
||||
@@ -4210,6 +4216,49 @@ public class NotificationManagerService extends SystemService {
|
||||
new NotificationShellCmd(NotificationManagerService.this)
|
||||
.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
|
||||
@@ -4425,6 +4474,15 @@ public class NotificationManagerService extends SystemService {
|
||||
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) {
|
||||
final ProtoOutputStream proto = new ProtoOutputStream(fd);
|
||||
synchronized (mNotificationLock) {
|
||||
@@ -8559,6 +8617,7 @@ public class NotificationManagerService extends SystemService {
|
||||
public boolean zen;
|
||||
public long since;
|
||||
public boolean stats;
|
||||
public boolean rvStats;
|
||||
public boolean redact = true;
|
||||
public boolean proto = false;
|
||||
public boolean criticalPriority = false;
|
||||
@@ -8594,6 +8653,14 @@ public class NotificationManagerService extends SystemService {
|
||||
} else {
|
||||
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)) {
|
||||
// Bugreport will call the service twice with priority arguments, first to dump
|
||||
// critical sections and then non critical ones. Set approriate filters
|
||||
|
||||
@@ -149,6 +149,7 @@ public class NotificationUsageStats {
|
||||
stats.numPostedByApp++;
|
||||
stats.updateInterarrivalEstimate(now);
|
||||
stats.countApiUse(notification);
|
||||
stats.numUndecoratedRemoteViews += (isUndecoratedRemoteView(notification) ? 1 : 0);
|
||||
}
|
||||
releaseAggregatedStatsLocked(aggregatedStatsArray);
|
||||
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.
|
||||
*/
|
||||
@@ -326,6 +334,15 @@ public class NotificationUsageStats {
|
||||
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) {
|
||||
if (ENABLE_AGGREGATED_IN_MEMORY_STATS) {
|
||||
for (AggregatedStats as : mStats.values()) {
|
||||
@@ -403,6 +420,7 @@ public class NotificationUsageStats {
|
||||
public int numRateViolations;
|
||||
public int numAlertViolations;
|
||||
public int numQuotaViolations;
|
||||
public int numUndecoratedRemoteViews;
|
||||
public long mLastAccessTime;
|
||||
|
||||
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(quietImportance.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("}");
|
||||
return output.toString();
|
||||
}
|
||||
@@ -1027,7 +1047,7 @@ public class NotificationUsageStats {
|
||||
private static final int MSG_DISMISS = 4;
|
||||
|
||||
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. */
|
||||
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_AIRTIME_EXPANDED_MS = "expansion_airtime_ms";
|
||||
private static final String COL_EXPAND_COUNT = "expansion_count";
|
||||
private static final String COL_UNDECORATED = "undecorated";
|
||||
|
||||
|
||||
private static final int EVENT_TYPE_POST = 1;
|
||||
@@ -1085,12 +1106,20 @@ public class NotificationUsageStats {
|
||||
"COUNT(*) AS cnt, " +
|
||||
"SUM(" + COL_MUTED + ") as muted, " +
|
||||
"SUM(" + COL_NOISY + ") as noisy, " +
|
||||
"SUM(" + COL_DEMOTED + ") as demoted " +
|
||||
"SUM(" + COL_DEMOTED + ") as demoted, " +
|
||||
"SUM(" + COL_UNDECORATED + ") as undecorated " +
|
||||
"FROM " + TAB_LOG + " " +
|
||||
"WHERE " +
|
||||
COL_EVENT_TYPE + "=" + EVENT_TYPE_POST +
|
||||
" AND " + COL_EVENT_TIME + " > %d " +
|
||||
" 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) {
|
||||
HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log",
|
||||
@@ -1146,7 +1175,8 @@ public class NotificationUsageStats {
|
||||
COL_AIRTIME_MS + " INT," +
|
||||
COL_FIRST_EXPANSIONTIME_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 {
|
||||
putPosttimeVisibility(r, cv);
|
||||
}
|
||||
cv.put(COL_UNDECORATED, (isUndecoratedRemoteView(r) ? 1 : 0));
|
||||
SQLiteDatabase db = mHelper.getWritableDatabase();
|
||||
if (db.insert(TAB_LOG, null, cv) < 0) {
|
||||
Log.wtf(TAG, "Error while trying to insert values: " + cv);
|
||||
@@ -1332,5 +1363,22 @@ public class NotificationUsageStats {
|
||||
}
|
||||
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.HistoricalPackageOps;
|
||||
import android.app.AppOpsManager.HistoricalUidOps;
|
||||
import android.app.INotificationManager;
|
||||
import android.app.ProcessMemoryState;
|
||||
import android.app.StatsManager;
|
||||
import android.bluetooth.BluetoothActivityEnergyInfo;
|
||||
@@ -140,6 +141,7 @@ import com.android.server.SystemService;
|
||||
import com.android.server.SystemServiceManager;
|
||||
import com.android.server.am.MemoryStatUtil.IonAllocations;
|
||||
import com.android.server.am.MemoryStatUtil.MemoryStat;
|
||||
import com.android.server.notification.NotificationManagerService;
|
||||
import com.android.server.role.RoleManagerInternal;
|
||||
import com.android.server.storage.DiskStatsFileLogger;
|
||||
import com.android.server.storage.DiskStatsLoggingService;
|
||||
@@ -1625,14 +1627,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
if (statsFiles.size() != 1) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
unpackStreamedData(tagId, elapsedNanos, wallClockNanos, pulledData, statsFiles);
|
||||
new File(mBaseDir.getAbsolutePath() + "/" + section + "_"
|
||||
+ lastHighWaterMark).delete();
|
||||
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 {
|
||||
int pos = 0;
|
||||
final int initialAvail = stream.available();
|
||||
@@ -2477,6 +2518,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
pullAppOps(elapsedNanos, wallClockNanos, ret);
|
||||
break;
|
||||
}
|
||||
case StatsLog.NOTIFICATION_REMOTE_VIEWS: {
|
||||
pullNotificationStats(NotificationManagerService.REPORT_REMOTE_VIEWS,
|
||||
tagId, elapsedNanos, wallClockNanos, ret);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Slog.w(TAG, "No such tagId data as " + tagId);
|
||||
return null;
|
||||
|
||||
@@ -20,6 +20,7 @@ android_test {
|
||||
"androidx.test.rules", "hamcrest-library",
|
||||
"mockito-target-inline-minus-junit4",
|
||||
"platform-test-annotations",
|
||||
"platformprotosnano",
|
||||
"hamcrest-library",
|
||||
"testables",
|
||||
"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