am 720a5c51: Merge "Add dumpsys output to UsageStatsService, along with --checkin support" into lmp-mr1-dev
* commit '720a5c51beb5beab442832ea8d169aa5b4edb43f': Add dumpsys output to UsageStatsService, along with --checkin support
This commit is contained in:
@@ -1371,7 +1371,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config.locale.getLanguage().isEmpty()) {
|
if (config.locale != null && !config.locale.getLanguage().isEmpty()) {
|
||||||
parts.add(localeToResourceQualifier(config.locale));
|
parts.add(localeToResourceQualifier(config.locale));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class UsageStatsDatabase {
|
|||||||
|
|
||||||
private static final String TAG = "UsageStatsDatabase";
|
private static final String TAG = "UsageStatsDatabase";
|
||||||
private static final boolean DEBUG = UsageStatsService.DEBUG;
|
private static final boolean DEBUG = UsageStatsService.DEBUG;
|
||||||
|
private static final String BAK_SUFFIX = ".bak";
|
||||||
|
|
||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
private final File[] mIntervalDirs;
|
private final File[] mIntervalDirs;
|
||||||
@@ -95,11 +96,71 @@ class UsageStatsDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface CheckinAction {
|
||||||
|
boolean checkin(IntervalStats stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls {@link CheckinAction#checkin(IntervalStats)} on the given {@link CheckinAction}
|
||||||
|
* for all {@link IntervalStats} that haven't been checked-in.
|
||||||
|
* If any of the calls to {@link CheckinAction#checkin(IntervalStats)} returns false or throws
|
||||||
|
* an exception, the check-in will be aborted.
|
||||||
|
*
|
||||||
|
* @param checkinAction The callback to run when checking-in {@link IntervalStats}.
|
||||||
|
* @return true if the check-in succeeded.
|
||||||
|
*/
|
||||||
|
public boolean checkinDailyFiles(CheckinAction checkinAction) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
final TimeSparseArray<AtomicFile> files =
|
||||||
|
mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY];
|
||||||
|
final int fileCount = files.size();
|
||||||
|
int start = 0;
|
||||||
|
while (start < fileCount - 1) {
|
||||||
|
if (!files.valueAt(start).getBaseFile().getName().endsWith("-c")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start == fileCount - 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
IntervalStats stats = new IntervalStats();
|
||||||
|
for (int i = start; i < fileCount - 1; i++) {
|
||||||
|
UsageStatsXml.read(files.valueAt(i), stats);
|
||||||
|
if (!checkinAction.checkin(stats)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(TAG, "Failed to check-in", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have successfully checked-in the stats, so rename the files so that they
|
||||||
|
// are marked as checked-in.
|
||||||
|
for (int i = start; i < fileCount - 1; i++) {
|
||||||
|
final AtomicFile file = files.valueAt(i);
|
||||||
|
final File checkedInFile = new File(file.getBaseFile().getParent(),
|
||||||
|
file.getBaseFile().getName() + "-c");
|
||||||
|
if (!file.getBaseFile().renameTo(checkedInFile)) {
|
||||||
|
// We must return success, as we've already marked some files as checked-in.
|
||||||
|
// It's better to repeat ourselves than to lose data.
|
||||||
|
Slog.e(TAG, "Failed to mark file " + file.getBaseFile().getPath()
|
||||||
|
+ " as checked-in");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void indexFilesLocked() {
|
private void indexFilesLocked() {
|
||||||
final FilenameFilter backupFileFilter = new FilenameFilter() {
|
final FilenameFilter backupFileFilter = new FilenameFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(File dir, String name) {
|
public boolean accept(File dir, String name) {
|
||||||
return !name.endsWith(".bak");
|
return !name.endsWith(BAK_SUFFIX);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -383,10 +444,10 @@ class UsageStatsDatabase {
|
|||||||
if (files != null) {
|
if (files != null) {
|
||||||
for (File f : files) {
|
for (File f : files) {
|
||||||
String path = f.getPath();
|
String path = f.getPath();
|
||||||
if (path.endsWith(".bak")) {
|
if (path.endsWith(BAK_SUFFIX)) {
|
||||||
f = new File(path.substring(0, path.length() - 4));
|
f = new File(path.substring(0, path.length() - BAK_SUFFIX.length()));
|
||||||
}
|
}
|
||||||
long beginTime = Long.parseLong(f.getName());
|
long beginTime = UsageStatsXml.parseBeginTime(f);
|
||||||
if (beginTime < expiryTime) {
|
if (beginTime < expiryTime) {
|
||||||
new AtomicFile(f).delete();
|
new AtomicFile(f).delete();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import android.content.pm.ParceledListSlice;
|
|||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Debug;
|
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@@ -48,9 +47,12 @@ import android.util.Slog;
|
|||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.internal.os.BackgroundThread;
|
import com.android.internal.os.BackgroundThread;
|
||||||
|
import com.android.internal.util.IndentingPrintWriter;
|
||||||
import com.android.server.SystemService;
|
import com.android.server.SystemService;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.PrintWriter;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -177,7 +179,7 @@ public class UsageStatsService extends SystemService implements
|
|||||||
long currentTimeMillis) {
|
long currentTimeMillis) {
|
||||||
UserUsageStatsService service = mUserState.get(userId);
|
UserUsageStatsService service = mUserState.get(userId);
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
service = new UserUsageStatsService(userId,
|
service = new UserUsageStatsService(getContext(), userId,
|
||||||
new File(mUsageStatsDir, Integer.toString(userId)), this);
|
new File(mUsageStatsDir, Integer.toString(userId)), this);
|
||||||
service.init(currentTimeMillis);
|
service.init(currentTimeMillis);
|
||||||
mUserState.put(userId, service);
|
mUserState.put(userId, service);
|
||||||
@@ -320,6 +322,30 @@ public class UsageStatsService extends SystemService implements
|
|||||||
mHandler.removeMessages(MSG_FLUSH_TO_DISK);
|
mHandler.removeMessages(MSG_FLUSH_TO_DISK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the Binder stub.
|
||||||
|
*/
|
||||||
|
void dump(String[] args, PrintWriter pw) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " ");
|
||||||
|
ArraySet<String> argSet = new ArraySet<>();
|
||||||
|
argSet.addAll(Arrays.asList(args));
|
||||||
|
|
||||||
|
final int userCount = mUserState.size();
|
||||||
|
for (int i = 0; i < userCount; i++) {
|
||||||
|
idpw.printPair("user", mUserState.keyAt(i));
|
||||||
|
idpw.println();
|
||||||
|
idpw.increaseIndent();
|
||||||
|
if (argSet.contains("--checkin")) {
|
||||||
|
mUserState.valueAt(i).checkin(idpw);
|
||||||
|
} else {
|
||||||
|
mUserState.valueAt(i).dump(idpw);
|
||||||
|
}
|
||||||
|
idpw.decreaseIndent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class H extends Handler {
|
class H extends Handler {
|
||||||
public H(Looper looper) {
|
public H(Looper looper) {
|
||||||
super(looper);
|
super(looper);
|
||||||
@@ -422,6 +448,18 @@ public class UsageStatsService extends SystemService implements
|
|||||||
Binder.restoreCallingIdentity(token);
|
Binder.restoreCallingIdentity(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||||
|
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED) {
|
||||||
|
pw.println("Permission Denial: can't dump UsageStats from pid="
|
||||||
|
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
|
||||||
|
+ " without permission " + android.Manifest.permission.DUMP);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UsageStatsService.this.dump(args, pw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -24,21 +24,26 @@ import com.android.internal.util.XmlUtils;
|
|||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.*;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
public class UsageStatsXml {
|
public class UsageStatsXml {
|
||||||
private static final String TAG = "UsageStatsXml";
|
private static final String TAG = "UsageStatsXml";
|
||||||
private static final int CURRENT_VERSION = 1;
|
private static final int CURRENT_VERSION = 1;
|
||||||
private static final String USAGESTATS_TAG = "usagestats";
|
private static final String USAGESTATS_TAG = "usagestats";
|
||||||
private static final String VERSION_ATTR = "version";
|
private static final String VERSION_ATTR = "version";
|
||||||
|
private static final String CHECKED_IN_SUFFIX = "-c";
|
||||||
|
|
||||||
public static long parseBeginTime(AtomicFile file) {
|
public static long parseBeginTime(AtomicFile file) {
|
||||||
return Long.parseLong(file.getBaseFile().getName());
|
return parseBeginTime(file.getBaseFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long parseBeginTime(File file) {
|
||||||
|
final String name = file.getName();
|
||||||
|
if (name.endsWith(CHECKED_IN_SUFFIX)) {
|
||||||
|
return Long.parseLong(
|
||||||
|
name.substring(0, name.length() - CHECKED_IN_SUFFIX.length()));
|
||||||
|
}
|
||||||
|
return Long.parseLong(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void read(AtomicFile file, IntervalStats statsOut) throws IOException {
|
public static void read(AtomicFile file, IntervalStats statsOut) throws IOException {
|
||||||
|
|||||||
@@ -23,9 +23,13 @@ import android.app.usage.UsageStats;
|
|||||||
import android.app.usage.UsageStatsManager;
|
import android.app.usage.UsageStatsManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
|
import com.android.internal.util.IndentingPrintWriter;
|
||||||
import com.android.server.usage.UsageStatsDatabase.StatCombiner;
|
import com.android.server.usage.UsageStatsDatabase.StatCombiner;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -43,7 +47,13 @@ class UserUsageStatsService {
|
|||||||
private static final String TAG = "UsageStatsService";
|
private static final String TAG = "UsageStatsService";
|
||||||
private static final boolean DEBUG = UsageStatsService.DEBUG;
|
private static final boolean DEBUG = UsageStatsService.DEBUG;
|
||||||
private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
private static final int sDateFormatFlags =
|
||||||
|
DateUtils.FORMAT_SHOW_DATE
|
||||||
|
| DateUtils.FORMAT_SHOW_TIME
|
||||||
|
| DateUtils.FORMAT_SHOW_YEAR
|
||||||
|
| DateUtils.FORMAT_NUMERIC_DATE;
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
private final UsageStatsDatabase mDatabase;
|
private final UsageStatsDatabase mDatabase;
|
||||||
private final IntervalStats[] mCurrentStats;
|
private final IntervalStats[] mCurrentStats;
|
||||||
private boolean mStatsChanged = false;
|
private boolean mStatsChanged = false;
|
||||||
@@ -55,7 +65,8 @@ class UserUsageStatsService {
|
|||||||
void onStatsUpdated();
|
void onStatsUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
UserUsageStatsService(int userId, File usageStatsDir, StatsUpdatedListener listener) {
|
UserUsageStatsService(Context context, int userId, File usageStatsDir, StatsUpdatedListener listener) {
|
||||||
|
mContext = context;
|
||||||
mDailyExpiryDate = new UnixCalendar(0);
|
mDailyExpiryDate = new UnixCalendar(0);
|
||||||
mDatabase = new UsageStatsDatabase(usageStatsDir);
|
mDatabase = new UsageStatsDatabase(usageStatsDir);
|
||||||
mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT];
|
mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT];
|
||||||
@@ -433,6 +444,117 @@ class UserUsageStatsService {
|
|||||||
tempCal.getTimeInMillis() + ")");
|
tempCal.getTimeInMillis() + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// -- DUMP related methods --
|
||||||
|
//
|
||||||
|
|
||||||
|
void checkin(final IndentingPrintWriter pw) {
|
||||||
|
mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() {
|
||||||
|
@Override
|
||||||
|
public boolean checkin(IntervalStats stats) {
|
||||||
|
printIntervalStats(pw, stats, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump(IndentingPrintWriter pw) {
|
||||||
|
// This is not a check-in, only dump in-memory stats.
|
||||||
|
for (int interval = 0; interval < mCurrentStats.length; interval++) {
|
||||||
|
pw.print("In-memory ");
|
||||||
|
pw.print(intervalToString(interval));
|
||||||
|
pw.println(" stats");
|
||||||
|
printIntervalStats(pw, mCurrentStats[interval], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatDateTime(long dateTime, boolean pretty) {
|
||||||
|
if (pretty) {
|
||||||
|
return "\"" + DateUtils.formatDateTime(mContext, dateTime, sDateFormatFlags) + "\"";
|
||||||
|
}
|
||||||
|
return Long.toString(dateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatElapsedTime(long elapsedTime, boolean pretty) {
|
||||||
|
if (pretty) {
|
||||||
|
return "\"" + DateUtils.formatElapsedTime(elapsedTime / 1000) + "\"";
|
||||||
|
}
|
||||||
|
return Long.toString(elapsedTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, boolean prettyDates) {
|
||||||
|
if (prettyDates) {
|
||||||
|
pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
|
||||||
|
stats.beginTime, stats.endTime, sDateFormatFlags) + "\"");
|
||||||
|
} else {
|
||||||
|
pw.printPair("beginTime", stats.beginTime);
|
||||||
|
pw.printPair("endTime", stats.endTime);
|
||||||
|
}
|
||||||
|
pw.println();
|
||||||
|
pw.increaseIndent();
|
||||||
|
pw.println("packages");
|
||||||
|
pw.increaseIndent();
|
||||||
|
final ArrayMap<String, UsageStats> pkgStats = stats.packageStats;
|
||||||
|
final int pkgCount = pkgStats.size();
|
||||||
|
for (int i = 0; i < pkgCount; i++) {
|
||||||
|
final UsageStats usageStats = pkgStats.valueAt(i);
|
||||||
|
pw.printPair("package", usageStats.mPackageName);
|
||||||
|
pw.printPair("totalTime", formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
|
||||||
|
pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
|
||||||
|
pw.println();
|
||||||
|
}
|
||||||
|
pw.decreaseIndent();
|
||||||
|
|
||||||
|
pw.println("configurations");
|
||||||
|
pw.increaseIndent();
|
||||||
|
final ArrayMap<Configuration, ConfigurationStats> configStats =
|
||||||
|
stats.configurations;
|
||||||
|
final int configCount = configStats.size();
|
||||||
|
for (int i = 0; i < configCount; i++) {
|
||||||
|
final ConfigurationStats config = configStats.valueAt(i);
|
||||||
|
pw.printPair("config", Configuration.resourceQualifierString(config.mConfiguration));
|
||||||
|
pw.printPair("totalTime", formatElapsedTime(config.mTotalTimeActive, prettyDates));
|
||||||
|
pw.printPair("lastTime", formatDateTime(config.mLastTimeActive, prettyDates));
|
||||||
|
pw.printPair("count", config.mActivationCount);
|
||||||
|
pw.println();
|
||||||
|
}
|
||||||
|
pw.decreaseIndent();
|
||||||
|
|
||||||
|
pw.println("events");
|
||||||
|
pw.increaseIndent();
|
||||||
|
final TimeSparseArray<UsageEvents.Event> events = stats.events;
|
||||||
|
final int eventCount = events != null ? events.size() : 0;
|
||||||
|
for (int i = 0; i < eventCount; i++) {
|
||||||
|
final UsageEvents.Event event = events.valueAt(i);
|
||||||
|
pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates));
|
||||||
|
pw.printPair("type", eventToString(event.mEventType));
|
||||||
|
pw.printPair("package", event.mPackage);
|
||||||
|
if (event.mClass != null) {
|
||||||
|
pw.printPair("class", event.mClass);
|
||||||
|
}
|
||||||
|
if (event.mConfiguration != null) {
|
||||||
|
pw.printPair("config", Configuration.resourceQualifierString(event.mConfiguration));
|
||||||
|
}
|
||||||
|
pw.println();
|
||||||
|
}
|
||||||
|
pw.decreaseIndent();
|
||||||
|
pw.decreaseIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String intervalToString(int interval) {
|
||||||
|
switch (interval) {
|
||||||
|
case UsageStatsManager.INTERVAL_DAILY:
|
||||||
|
return "daily";
|
||||||
|
case UsageStatsManager.INTERVAL_WEEKLY:
|
||||||
|
return "weekly";
|
||||||
|
case UsageStatsManager.INTERVAL_MONTHLY:
|
||||||
|
return "monthly";
|
||||||
|
case UsageStatsManager.INTERVAL_YEARLY:
|
||||||
|
return "yearly";
|
||||||
|
default:
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String eventToString(int eventType) {
|
private static String eventToString(int eventType) {
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
|
|||||||
Reference in New Issue
Block a user