Loading core/java/android/content/res/Configuration.java +1 −1 Original line number Diff line number Diff line Loading @@ -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)); } Loading services/usage/java/com/android/server/usage/UsageStatsDatabase.java +65 −4 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ class UsageStatsDatabase { private static final String TAG = "UsageStatsDatabase"; private static final boolean DEBUG = UsageStatsService.DEBUG; private static final String BAK_SUFFIX = ".bak"; private final Object mLock = new Object(); private final File[] mIntervalDirs; Loading Loading @@ -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() { final FilenameFilter backupFileFilter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { return !name.endsWith(".bak"); return !name.endsWith(BAK_SUFFIX); } }; Loading Loading @@ -383,10 +444,10 @@ class UsageStatsDatabase { if (files != null) { for (File f : files) { String path = f.getPath(); if (path.endsWith(".bak")) { f = new File(path.substring(0, path.length() - 4)); if (path.endsWith(BAK_SUFFIX)) { 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) { new AtomicFile(f).delete(); } Loading services/usage/java/com/android/server/usage/UsageStatsService.java +40 −2 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.os.Binder; import android.os.Debug; import android.os.Environment; import android.os.Handler; import android.os.Looper; Loading @@ -48,9 +47,12 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; import com.android.server.SystemService; import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; import java.util.List; Loading Loading @@ -177,7 +179,7 @@ public class UsageStatsService extends SystemService implements long currentTimeMillis) { UserUsageStatsService service = mUserState.get(userId); if (service == null) { service = new UserUsageStatsService(userId, service = new UserUsageStatsService(getContext(), userId, new File(mUsageStatsDir, Integer.toString(userId)), this); service.init(currentTimeMillis); mUserState.put(userId, service); Loading Loading @@ -320,6 +322,30 @@ public class UsageStatsService extends SystemService implements 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 { public H(Looper looper) { super(looper); Loading Loading @@ -422,6 +448,18 @@ public class UsageStatsService extends SystemService implements 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); } } /** Loading services/usage/java/com/android/server/usage/UsageStatsXml.java +12 −7 Original line number Diff line number Diff line Loading @@ -24,21 +24,26 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.*; public class UsageStatsXml { private static final String TAG = "UsageStatsXml"; private static final int CURRENT_VERSION = 1; private static final String USAGESTATS_TAG = "usagestats"; private static final String VERSION_ATTR = "version"; private static final String CHECKED_IN_SUFFIX = "-c"; 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 { Loading services/usage/java/com/android/server/usage/UserUsageStatsService.java +123 −1 Original line number Diff line number Diff line Loading @@ -23,9 +23,13 @@ import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; import android.content.res.Configuration; import android.os.SystemClock; import android.content.Context; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; import com.android.server.usage.UsageStatsDatabase.StatCombiner; import java.io.File; Loading @@ -43,7 +47,13 @@ class UserUsageStatsService { private static final String TAG = "UsageStatsService"; private static final boolean DEBUG = UsageStatsService.DEBUG; 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 IntervalStats[] mCurrentStats; private boolean mStatsChanged = false; Loading @@ -55,7 +65,8 @@ class UserUsageStatsService { void onStatsUpdated(); } UserUsageStatsService(int userId, File usageStatsDir, StatsUpdatedListener listener) { UserUsageStatsService(Context context, int userId, File usageStatsDir, StatsUpdatedListener listener) { mContext = context; mDailyExpiryDate = new UnixCalendar(0); mDatabase = new UsageStatsDatabase(usageStatsDir); mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT]; Loading Loading @@ -433,6 +444,117 @@ class UserUsageStatsService { 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) { switch (eventType) { Loading Loading
core/java/android/content/res/Configuration.java +1 −1 Original line number Diff line number Diff line Loading @@ -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)); } Loading
services/usage/java/com/android/server/usage/UsageStatsDatabase.java +65 −4 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ class UsageStatsDatabase { private static final String TAG = "UsageStatsDatabase"; private static final boolean DEBUG = UsageStatsService.DEBUG; private static final String BAK_SUFFIX = ".bak"; private final Object mLock = new Object(); private final File[] mIntervalDirs; Loading Loading @@ -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() { final FilenameFilter backupFileFilter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { return !name.endsWith(".bak"); return !name.endsWith(BAK_SUFFIX); } }; Loading Loading @@ -383,10 +444,10 @@ class UsageStatsDatabase { if (files != null) { for (File f : files) { String path = f.getPath(); if (path.endsWith(".bak")) { f = new File(path.substring(0, path.length() - 4)); if (path.endsWith(BAK_SUFFIX)) { 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) { new AtomicFile(f).delete(); } Loading
services/usage/java/com/android/server/usage/UsageStatsService.java +40 −2 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.os.Binder; import android.os.Debug; import android.os.Environment; import android.os.Handler; import android.os.Looper; Loading @@ -48,9 +47,12 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; import com.android.server.SystemService; import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; import java.util.List; Loading Loading @@ -177,7 +179,7 @@ public class UsageStatsService extends SystemService implements long currentTimeMillis) { UserUsageStatsService service = mUserState.get(userId); if (service == null) { service = new UserUsageStatsService(userId, service = new UserUsageStatsService(getContext(), userId, new File(mUsageStatsDir, Integer.toString(userId)), this); service.init(currentTimeMillis); mUserState.put(userId, service); Loading Loading @@ -320,6 +322,30 @@ public class UsageStatsService extends SystemService implements 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 { public H(Looper looper) { super(looper); Loading Loading @@ -422,6 +448,18 @@ public class UsageStatsService extends SystemService implements 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); } } /** Loading
services/usage/java/com/android/server/usage/UsageStatsXml.java +12 −7 Original line number Diff line number Diff line Loading @@ -24,21 +24,26 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.*; public class UsageStatsXml { private static final String TAG = "UsageStatsXml"; private static final int CURRENT_VERSION = 1; private static final String USAGESTATS_TAG = "usagestats"; private static final String VERSION_ATTR = "version"; private static final String CHECKED_IN_SUFFIX = "-c"; 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 { Loading
services/usage/java/com/android/server/usage/UserUsageStatsService.java +123 −1 Original line number Diff line number Diff line Loading @@ -23,9 +23,13 @@ import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; import android.content.res.Configuration; import android.os.SystemClock; import android.content.Context; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; import com.android.server.usage.UsageStatsDatabase.StatCombiner; import java.io.File; Loading @@ -43,7 +47,13 @@ class UserUsageStatsService { private static final String TAG = "UsageStatsService"; private static final boolean DEBUG = UsageStatsService.DEBUG; 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 IntervalStats[] mCurrentStats; private boolean mStatsChanged = false; Loading @@ -55,7 +65,8 @@ class UserUsageStatsService { void onStatsUpdated(); } UserUsageStatsService(int userId, File usageStatsDir, StatsUpdatedListener listener) { UserUsageStatsService(Context context, int userId, File usageStatsDir, StatsUpdatedListener listener) { mContext = context; mDailyExpiryDate = new UnixCalendar(0); mDatabase = new UsageStatsDatabase(usageStatsDir); mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT]; Loading Loading @@ -433,6 +444,117 @@ class UserUsageStatsService { 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) { switch (eventType) { Loading