Fix issue #1769910 (Track activity launch times)
The usage stats service now collects per-activity launch time stats. There are a number of fixes and improvements to its statistics management and collection; it now operates its calendar in GMT and ensures that for checkin purposes it always reports one day and only one complete day to the checkin service. Also change the checkin option from "-c" to "--checkin" since it is really a special thing.
This commit is contained in:
@@ -37,11 +37,11 @@ import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* This service collects the statistics associated with usage
|
||||
@@ -53,6 +53,20 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
public static final String SERVICE_NAME = "usagestats";
|
||||
private static final boolean localLOGV = false;
|
||||
private static final String TAG = "UsageStats";
|
||||
|
||||
// Current on-disk Parcel version
|
||||
private static final int VERSION = 1003;
|
||||
|
||||
private static final int CHECKIN_VERSION = 2;
|
||||
|
||||
private static final String FILE_PREFIX = "usage-";
|
||||
|
||||
private static final int FILE_WRITE_INTERVAL = 30*60*1000; //ms
|
||||
|
||||
private static final int MAX_NUM_FILES = 5;
|
||||
|
||||
private static final int MAX_LAUNCH_TIME_SAMPLES = 50;
|
||||
|
||||
static IUsageStats sService;
|
||||
private Context mContext;
|
||||
// structure used to maintain statistics since the last checkin.
|
||||
@@ -66,16 +80,76 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
// Order of locks is mFileLock followed by mStatsLock to avoid deadlocks
|
||||
private String mResumedPkg;
|
||||
private File mFile;
|
||||
private String mFileLeaf;
|
||||
//private File mBackupFile;
|
||||
private long mLastWriteRealTime;
|
||||
private int _FILE_WRITE_INTERVAL = 30*60*1000; //ms
|
||||
private static final String _PREFIX_DELIMIT=".";
|
||||
private String mFilePrefix;
|
||||
private long mLastWriteElapsedTime;
|
||||
private File mDir;
|
||||
private Calendar mCal;
|
||||
private static final int _MAX_NUM_FILES = 10;
|
||||
private long mLastTime;
|
||||
private int mLastWriteDay;
|
||||
|
||||
static class TimeStats {
|
||||
int count;
|
||||
|
||||
boolean haveStats;
|
||||
|
||||
int samples;
|
||||
int minimum;
|
||||
int maximum;
|
||||
int average;
|
||||
int median;
|
||||
|
||||
int size;
|
||||
int[] array;
|
||||
|
||||
private int avail;
|
||||
|
||||
void add(int val) {
|
||||
count++;
|
||||
if (size > MAX_LAUNCH_TIME_SAMPLES) {
|
||||
return;
|
||||
}
|
||||
if (size >= avail) {
|
||||
avail = ((avail+2)*3)/2;
|
||||
int[] newarray = new int[avail];
|
||||
if (array != null) {
|
||||
System.arraycopy(array, 0, newarray, 0, size);
|
||||
}
|
||||
array = newarray;
|
||||
}
|
||||
array[size] = val;
|
||||
size++;
|
||||
haveStats = false;
|
||||
}
|
||||
|
||||
void computeStats() {
|
||||
if (haveStats) {
|
||||
return;
|
||||
}
|
||||
|
||||
average = 0;
|
||||
int i = samples = size;
|
||||
if (i > 0) {
|
||||
java.util.Arrays.sort(array, 0, i);
|
||||
i--;
|
||||
minimum = maximum = average = array[i];
|
||||
median = array[i/2];
|
||||
while (i > 0) {
|
||||
i--;
|
||||
int v = array[i];
|
||||
if (v < minimum) minimum = v;
|
||||
if (v > maximum) maximum = v;
|
||||
average += v;
|
||||
}
|
||||
average = average/size;
|
||||
} else {
|
||||
minimum = maximum = median = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PkgUsageStatsExtended {
|
||||
final HashMap<String, TimeStats> mLaunchTimes
|
||||
= new HashMap<String, TimeStats>();
|
||||
int mLaunchCount;
|
||||
long mUsageTime;
|
||||
long mPausedTime;
|
||||
@@ -85,44 +159,142 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
mLaunchCount = 0;
|
||||
mUsageTime = 0;
|
||||
}
|
||||
|
||||
PkgUsageStatsExtended(Parcel in) {
|
||||
mLaunchCount = in.readInt();
|
||||
mUsageTime = in.readLong();
|
||||
if (localLOGV) Log.v(TAG, "Launch count: " + mLaunchCount
|
||||
+ ", Usage time:" + mUsageTime);
|
||||
|
||||
final int N = in.readInt();
|
||||
if (localLOGV) Log.v(TAG, "Reading comps: " + N);
|
||||
for (int i=0; i<N; i++) {
|
||||
String comp = in.readString();
|
||||
final int M = in.readInt();
|
||||
final int count = in.readInt();
|
||||
if (localLOGV) Log.v(TAG, "Component: " + comp + ", times: " + M);
|
||||
if (M > 0) {
|
||||
TimeStats times = new TimeStats();
|
||||
times.count = count;
|
||||
times.size = times.avail = M;
|
||||
times.array = new int[M];
|
||||
for (int j=0; j<M; j++) {
|
||||
times.array[j] = in.readInt();
|
||||
}
|
||||
mLaunchTimes.put(comp, times);
|
||||
} else if (M == -1) {
|
||||
TimeStats times = new TimeStats();
|
||||
times.count = count;
|
||||
times.samples = in.readInt();
|
||||
times.minimum = in.readInt();
|
||||
times.maximum = in.readInt();
|
||||
times.average = in.readInt();
|
||||
times.median = in.readInt();
|
||||
times.haveStats = true;
|
||||
mLaunchTimes.put(comp, times);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateResume() {
|
||||
mLaunchCount ++;
|
||||
mResumedTime = SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
void updatePause() {
|
||||
mPausedTime = SystemClock.elapsedRealtime();
|
||||
mUsageTime += (mPausedTime - mResumedTime);
|
||||
}
|
||||
|
||||
void addLaunchTime(String comp, int millis) {
|
||||
TimeStats times = mLaunchTimes.get(comp);
|
||||
if (times == null) {
|
||||
times = new TimeStats();
|
||||
mLaunchTimes.put(comp, times);
|
||||
}
|
||||
times.add(millis);
|
||||
}
|
||||
|
||||
void writeToParcel(Parcel out, boolean allTimes) {
|
||||
out.writeInt(mLaunchCount);
|
||||
out.writeLong(mUsageTime);
|
||||
final int N = mLaunchTimes.size();
|
||||
out.writeInt(N);
|
||||
if (N > 0) {
|
||||
for (Map.Entry<String, TimeStats> ent : mLaunchTimes.entrySet()) {
|
||||
out.writeString(ent.getKey());
|
||||
TimeStats times = ent.getValue();
|
||||
if (allTimes) {
|
||||
final int M = times.size;
|
||||
out.writeInt(M);
|
||||
out.writeInt(times.count);
|
||||
for (int j=0; j<M; j++) {
|
||||
out.writeInt(times.array[j]);
|
||||
}
|
||||
} else {
|
||||
times.computeStats();
|
||||
out.writeInt(-1);
|
||||
out.writeInt(times.count);
|
||||
out.writeInt(times.samples);
|
||||
out.writeInt(times.minimum);
|
||||
out.writeInt(times.maximum);
|
||||
out.writeInt(times.average);
|
||||
out.writeInt(times.median);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
mLaunchTimes.clear();
|
||||
mLaunchCount = 0;
|
||||
mUsageTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
UsageStatsService(String fileName) {
|
||||
UsageStatsService(String dir) {
|
||||
mStats = new HashMap<String, PkgUsageStatsExtended>();
|
||||
mStatsLock = new Object();
|
||||
mFileLock = new Object();
|
||||
mFilePrefix = fileName;
|
||||
mCal = Calendar.getInstance();
|
||||
mDir = new File(dir);
|
||||
mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
|
||||
|
||||
mDir.mkdir();
|
||||
|
||||
// Remove any old usage files from previous versions.
|
||||
File parentDir = mDir.getParentFile();
|
||||
String fList[] = parentDir.list();
|
||||
if (fList != null) {
|
||||
String prefix = mDir.getName() + ".";
|
||||
int i = fList.length;
|
||||
while (i > 0) {
|
||||
i--;
|
||||
if (fList[i].startsWith(prefix)) {
|
||||
Log.i(TAG, "Deleting old usage file: " + fList[i]);
|
||||
(new File(parentDir, fList[i])).delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update current stats which are binned by date
|
||||
String uFileName = getCurrentDateStr(mFilePrefix);
|
||||
mFile = new File(uFileName);
|
||||
mFileLeaf = getCurrentDateStr(FILE_PREFIX);
|
||||
mFile = new File(mDir, mFileLeaf);
|
||||
readStatsFromFile();
|
||||
mLastWriteRealTime = SystemClock.elapsedRealtime();
|
||||
mLastTime = new Date().getTime();
|
||||
mLastWriteElapsedTime = SystemClock.elapsedRealtime();
|
||||
// mCal was set by getCurrentDateStr(), want to use that same time.
|
||||
mLastWriteDay = mCal.get(Calendar.DAY_OF_YEAR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility method to convert date into string.
|
||||
*/
|
||||
private String getCurrentDateStr(String prefix) {
|
||||
mCal.setTime(new Date());
|
||||
mCal.setTimeInMillis(System.currentTimeMillis());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (prefix != null) {
|
||||
sb.append(prefix);
|
||||
sb.append(".");
|
||||
}
|
||||
sb.append(mCal.get(Calendar.YEAR));
|
||||
int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
|
||||
if (mm < 10) {
|
||||
sb.append("0");
|
||||
@@ -133,7 +305,6 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
sb.append("0");
|
||||
}
|
||||
sb.append(dd);
|
||||
sb.append(mCal.get(Calendar.YEAR));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -166,11 +337,20 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
|
||||
private void readStatsFLOCK(File file) throws IOException {
|
||||
Parcel in = getParcelForFile(file);
|
||||
while (in.dataAvail() > 0) {
|
||||
int vers = in.readInt();
|
||||
if (vers != VERSION) {
|
||||
Log.w(TAG, "Usage stats version changed; dropping");
|
||||
return;
|
||||
}
|
||||
int N = in.readInt();
|
||||
while (N > 0) {
|
||||
N--;
|
||||
String pkgName = in.readString();
|
||||
PkgUsageStatsExtended pus = new PkgUsageStatsExtended();
|
||||
pus.mLaunchCount = in.readInt();
|
||||
pus.mUsageTime = in.readLong();
|
||||
if (pkgName == null) {
|
||||
break;
|
||||
}
|
||||
if (localLOGV) Log.v(TAG, "Reading package #" + N + ": " + pkgName);
|
||||
PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
|
||||
synchronized (mStatsLock) {
|
||||
mStats.put(pkgName, pus);
|
||||
}
|
||||
@@ -178,27 +358,18 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
}
|
||||
|
||||
private ArrayList<String> getUsageStatsFileListFLOCK() {
|
||||
File dir = getUsageFilesDir();
|
||||
if (dir == null) {
|
||||
Log.w(TAG, "Couldnt find writable directory for usage stats file");
|
||||
return null;
|
||||
}
|
||||
// Check if there are too many files in the system and delete older files
|
||||
String fList[] = dir.list();
|
||||
String fList[] = mDir.list();
|
||||
if (fList == null) {
|
||||
return null;
|
||||
}
|
||||
File pre = new File(mFilePrefix);
|
||||
String filePrefix = pre.getName();
|
||||
// file name followed by dot
|
||||
int prefixLen = filePrefix.length()+1;
|
||||
ArrayList<String> fileList = new ArrayList<String>();
|
||||
for (String file : fList) {
|
||||
int index = file.indexOf(filePrefix);
|
||||
if (index == -1) {
|
||||
if (!file.startsWith(FILE_PREFIX)) {
|
||||
continue;
|
||||
}
|
||||
if (file.endsWith(".bak")) {
|
||||
(new File(mDir, file)).delete();
|
||||
continue;
|
||||
}
|
||||
fileList.add(file);
|
||||
@@ -206,20 +377,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
return fileList;
|
||||
}
|
||||
|
||||
private File getUsageFilesDir() {
|
||||
if (mFilePrefix == null) {
|
||||
return null;
|
||||
}
|
||||
File pre = new File(mFilePrefix);
|
||||
return new File(pre.getParent());
|
||||
}
|
||||
|
||||
private void checkFileLimitFLOCK() {
|
||||
File dir = getUsageFilesDir();
|
||||
if (dir == null) {
|
||||
Log.w(TAG, "Couldnt find writable directory for usage stats file");
|
||||
return;
|
||||
}
|
||||
// Get all usage stats output files
|
||||
ArrayList<String> fileList = getUsageStatsFileListFLOCK();
|
||||
if (fileList == null) {
|
||||
@@ -227,49 +385,54 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
return;
|
||||
}
|
||||
int count = fileList.size();
|
||||
if (count <= _MAX_NUM_FILES) {
|
||||
if (count <= MAX_NUM_FILES) {
|
||||
return;
|
||||
}
|
||||
// Sort files
|
||||
Collections.sort(fileList);
|
||||
count -= _MAX_NUM_FILES;
|
||||
count -= MAX_NUM_FILES;
|
||||
// Delete older files
|
||||
for (int i = 0; i < count; i++) {
|
||||
String fileName = fileList.get(i);
|
||||
File file = new File(dir, fileName);
|
||||
Log.i(TAG, "Deleting file : "+fileName);
|
||||
File file = new File(mDir, fileName);
|
||||
Log.i(TAG, "Deleting usage file : " + fileName);
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeStatsToFile() {
|
||||
private void writeStatsToFile(boolean force) {
|
||||
synchronized (mFileLock) {
|
||||
long currTime = new Date().getTime();
|
||||
boolean dayChanged = ((currTime - mLastTime) >= (24*60*60*1000));
|
||||
long currRealTime = SystemClock.elapsedRealtime();
|
||||
if (((currRealTime-mLastWriteRealTime) < _FILE_WRITE_INTERVAL) &&
|
||||
(!dayChanged)) {
|
||||
// wait till the next update
|
||||
return;
|
||||
mCal.setTimeInMillis(System.currentTimeMillis());
|
||||
final int curDay = mCal.get(Calendar.DAY_OF_YEAR);
|
||||
// Determine if the day changed... note that this will be wrong
|
||||
// if the year has changed but we are in the same day of year...
|
||||
// we can probably live with this.
|
||||
final boolean dayChanged = curDay != mLastWriteDay;
|
||||
long currElapsedTime = SystemClock.elapsedRealtime();
|
||||
if (!force) {
|
||||
if (((currElapsedTime-mLastWriteElapsedTime) < FILE_WRITE_INTERVAL) &&
|
||||
(!dayChanged)) {
|
||||
// wait till the next update
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Get the most recent file
|
||||
String todayStr = getCurrentDateStr(mFilePrefix);
|
||||
mFileLeaf = getCurrentDateStr(FILE_PREFIX);
|
||||
// Copy current file to back up
|
||||
File backupFile = new File(mFile.getPath() + ".bak");
|
||||
mFile.renameTo(backupFile);
|
||||
try {
|
||||
checkFileLimitFLOCK();
|
||||
mFile.createNewFile();
|
||||
// Write mStats to file
|
||||
writeStatsFLOCK();
|
||||
mLastWriteRealTime = currRealTime;
|
||||
mLastTime = currTime;
|
||||
writeStatsFLOCK(!dayChanged);
|
||||
mLastWriteElapsedTime = currElapsedTime;
|
||||
if (dayChanged) {
|
||||
mLastWriteDay = curDay;
|
||||
// clear stats
|
||||
synchronized (mStats) {
|
||||
mStats.clear();
|
||||
}
|
||||
mFile = new File(todayStr);
|
||||
mFile = new File(mDir, mFileLeaf);
|
||||
checkFileLimitFLOCK();
|
||||
}
|
||||
// Delete the backup file
|
||||
if (backupFile != null) {
|
||||
@@ -278,30 +441,35 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed writing stats to file:" + mFile);
|
||||
if (backupFile != null) {
|
||||
mFile.delete();
|
||||
backupFile.renameTo(mFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeStatsFLOCK() throws IOException {
|
||||
private void writeStatsFLOCK(boolean allTimes) throws IOException {
|
||||
FileOutputStream stream = new FileOutputStream(mFile);
|
||||
Parcel out = Parcel.obtain();
|
||||
writeStatsToParcelFLOCK(out);
|
||||
stream.write(out.marshall());
|
||||
out.recycle();
|
||||
stream.flush();
|
||||
stream.close();
|
||||
try {
|
||||
Parcel out = Parcel.obtain();
|
||||
writeStatsToParcelFLOCK(out, allTimes);
|
||||
stream.write(out.marshall());
|
||||
out.recycle();
|
||||
stream.flush();
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeStatsToParcelFLOCK(Parcel out) {
|
||||
private void writeStatsToParcelFLOCK(Parcel out, boolean allTimes) {
|
||||
synchronized (mStatsLock) {
|
||||
out.writeInt(VERSION);
|
||||
Set<String> keys = mStats.keySet();
|
||||
out.writeInt(keys.size());
|
||||
for (String key : keys) {
|
||||
PkgUsageStatsExtended pus = mStats.get(key);
|
||||
out.writeString(key);
|
||||
out.writeInt(pus.mLaunchCount);
|
||||
out.writeLong(pus.mUsageTime);
|
||||
pus.writeToParcel(out, allTimes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,6 +523,10 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
return;
|
||||
}
|
||||
if (localLOGV) Log.i(TAG, "paused component:"+pkgName);
|
||||
|
||||
// Persist current data to file if needed.
|
||||
writeStatsToFile(false);
|
||||
|
||||
synchronized (mStatsLock) {
|
||||
PkgUsageStatsExtended pus = mStats.get(pkgName);
|
||||
if (pus == null) {
|
||||
@@ -364,8 +536,25 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
}
|
||||
pus.updatePause();
|
||||
}
|
||||
// Persist data to file
|
||||
writeStatsToFile();
|
||||
}
|
||||
|
||||
public void noteLaunchTime(ComponentName componentName, int millis) {
|
||||
enforceCallingPermission();
|
||||
String pkgName;
|
||||
if ((componentName == null) ||
|
||||
((pkgName = componentName.getPackageName()) == null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Persist current data to file if needed.
|
||||
writeStatsToFile(false);
|
||||
|
||||
synchronized (mStatsLock) {
|
||||
PkgUsageStatsExtended pus = mStats.get(pkgName);
|
||||
if (pus != null) {
|
||||
pus.addLaunchTime(componentName.getClassName(), millis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void enforceCallingPermission() {
|
||||
@@ -432,27 +621,25 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void collectDumpInfoFLOCK(PrintWriter pw, String[] args) {
|
||||
private void collectDumpInfoFLOCK(PrintWriter pw, boolean isCompactOutput,
|
||||
boolean deleteAfterPrint) {
|
||||
List<String> fileList = getUsageStatsFileListFLOCK();
|
||||
if (fileList == null) {
|
||||
return;
|
||||
}
|
||||
final boolean isCheckinRequest = scanArgs(args, "-c");
|
||||
Collections.sort(fileList);
|
||||
File usageFile = new File(mFilePrefix);
|
||||
String dirName = usageFile.getParent();
|
||||
File dir = new File(dirName);
|
||||
String filePrefix = usageFile.getName();
|
||||
// file name followed by dot
|
||||
int prefixLen = filePrefix.length()+1;
|
||||
String todayStr = getCurrentDateStr(null);
|
||||
for (String file : fileList) {
|
||||
File dFile = new File(dir, file);
|
||||
String dateStr = file.substring(prefixLen);
|
||||
if (deleteAfterPrint && file.equalsIgnoreCase(mFileLeaf)) {
|
||||
// In this mode we don't print the current day's stats, since
|
||||
// they are incomplete.
|
||||
continue;
|
||||
}
|
||||
File dFile = new File(mDir, file);
|
||||
String dateStr = file.substring(FILE_PREFIX.length());
|
||||
try {
|
||||
Parcel in = getParcelForFile(dFile);
|
||||
collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCheckinRequest);
|
||||
if (isCheckinRequest && !todayStr.equalsIgnoreCase(dateStr)) {
|
||||
collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCompactOutput);
|
||||
if (deleteAfterPrint) {
|
||||
// Delete old file after collecting info only for checkin requests
|
||||
dFile.delete();
|
||||
}
|
||||
@@ -466,40 +653,102 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
}
|
||||
|
||||
private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw,
|
||||
String date, boolean isCheckinRequest) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Date:");
|
||||
sb.append(date);
|
||||
boolean first = true;
|
||||
while (in.dataAvail() > 0) {
|
||||
String pkgName = in.readString();
|
||||
int launchCount = in.readInt();
|
||||
long usageTime = in.readLong();
|
||||
if (isCheckinRequest) {
|
||||
if (!first) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(pkgName);
|
||||
sb.append(",");
|
||||
sb.append(launchCount);
|
||||
sb.append(",");
|
||||
sb.append(usageTime);
|
||||
sb.append("ms");
|
||||
} else {
|
||||
if (first) {
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append("pkg=");
|
||||
sb.append(pkgName);
|
||||
sb.append(", launchCount=");
|
||||
sb.append(launchCount);
|
||||
sb.append(", usageTime=");
|
||||
sb.append(usageTime);
|
||||
sb.append(" ms\n");
|
||||
}
|
||||
first = false;
|
||||
String date, boolean isCompactOutput) {
|
||||
StringBuilder sb = new StringBuilder(512);
|
||||
if (isCompactOutput) {
|
||||
sb.append("D:");
|
||||
sb.append(CHECKIN_VERSION);
|
||||
sb.append(',');
|
||||
} else {
|
||||
sb.append("Date: ");
|
||||
}
|
||||
|
||||
sb.append(date);
|
||||
|
||||
int vers = in.readInt();
|
||||
if (vers != VERSION) {
|
||||
sb.append(" (old data version)");
|
||||
pw.println(sb.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
pw.println(sb.toString());
|
||||
int N = in.readInt();
|
||||
|
||||
while (N > 0) {
|
||||
N--;
|
||||
String pkgName = in.readString();
|
||||
if (pkgName == null) {
|
||||
break;
|
||||
}
|
||||
sb.setLength(0);
|
||||
PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
|
||||
if (isCompactOutput) {
|
||||
sb.append("P:");
|
||||
sb.append(pkgName);
|
||||
sb.append(",");
|
||||
sb.append(pus.mLaunchCount);
|
||||
sb.append(",");
|
||||
sb.append(pus.mUsageTime);
|
||||
sb.append('\n');
|
||||
final int NC = pus.mLaunchTimes.size();
|
||||
if (NC > 0) {
|
||||
for (Map.Entry<String, TimeStats> ent : pus.mLaunchTimes.entrySet()) {
|
||||
sb.append("A:");
|
||||
sb.append(ent.getKey());
|
||||
sb.append(",");
|
||||
TimeStats times = ent.getValue();
|
||||
times.computeStats();
|
||||
sb.append(times.count);
|
||||
sb.append(",");
|
||||
sb.append(times.samples);
|
||||
sb.append(",");
|
||||
sb.append(times.minimum);
|
||||
sb.append(",");
|
||||
sb.append(times.maximum);
|
||||
sb.append(",");
|
||||
sb.append(times.average);
|
||||
sb.append(",");
|
||||
sb.append(times.median);
|
||||
sb.append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
sb.append(" ");
|
||||
sb.append(pkgName);
|
||||
sb.append(": ");
|
||||
sb.append(pus.mLaunchCount);
|
||||
sb.append(" times, ");
|
||||
sb.append(pus.mUsageTime);
|
||||
sb.append(" ms");
|
||||
sb.append('\n');
|
||||
final int NC = pus.mLaunchTimes.size();
|
||||
if (NC > 0) {
|
||||
for (Map.Entry<String, TimeStats> ent : pus.mLaunchTimes.entrySet()) {
|
||||
sb.append(" ");
|
||||
sb.append(ent.getKey());
|
||||
TimeStats times = ent.getValue();
|
||||
times.computeStats();
|
||||
sb.append(": count=");
|
||||
sb.append(times.count);
|
||||
sb.append(", samples=");
|
||||
sb.append(times.samples);
|
||||
sb.append(", min=");
|
||||
sb.append(times.minimum);
|
||||
sb.append(", max=");
|
||||
sb.append(times.maximum);
|
||||
sb.append(", avg=");
|
||||
sb.append(times.average);
|
||||
sb.append(", med=");
|
||||
sb.append(times.median);
|
||||
sb.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pw.write(sb.toString());
|
||||
}
|
||||
pw.write(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -524,8 +773,19 @@ public final class UsageStatsService extends IUsageStats.Stub {
|
||||
* The data persisted to file is parsed and the stats are computed.
|
||||
*/
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
final boolean isCheckinRequest = scanArgs(args, "--checkin");
|
||||
final boolean isCompactOutput = isCheckinRequest || scanArgs(args, "-c");
|
||||
final boolean deleteAfterPrint = isCheckinRequest || scanArgs(args, "-d");
|
||||
|
||||
// Make sure the current stats are written to the file. This
|
||||
// doesn't need to be done if we are deleting files after printing,
|
||||
// since it that case we won't print the current stats.
|
||||
if (!deleteAfterPrint) {
|
||||
writeStatsToFile(true);
|
||||
}
|
||||
|
||||
synchronized (mFileLock) {
|
||||
collectDumpInfoFLOCK(pw, args);
|
||||
collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user