Loading core/java/android/os/BatteryStats.java +91 −12 Original line number Diff line number Diff line Loading @@ -6948,8 +6948,9 @@ public abstract class BatteryStats { // This constant MUST be incremented whenever the history dump format changes. private static final int FORMAT_VERSION = 2; private final SimpleDateFormat mHistoryItemTimestampFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US); private final boolean mPerformanceBaseline; private final HistoryLogTimeFormatter mHistoryLogTimeFormatter; private final SimpleDateFormat mHistoryItemTimestampFormat; private final SimpleDateFormat mCurrentTimeEventTimeFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US); Loading @@ -6960,6 +6961,7 @@ public abstract class BatteryStats { private final int mFormatVersion; private final StringBuilder mStringBuilder = new StringBuilder(); int oldState = 0; int oldState2 = 0; int oldLevel = -1; Loading @@ -6974,18 +6976,30 @@ public abstract class BatteryStats { long lastTime = -1; public HistoryPrinter() { this(TimeZone.getDefault()); this(0); } public HistoryPrinter(TimeZone timeZone) { public HistoryPrinter(int flags) { this(TimeZone.getDefault(), flags); } public HistoryPrinter(TimeZone timeZone, int flags) { this(com.android.server.power.optimization.Flags .extendedBatteryHistoryContinuousCollectionEnabled() ? FORMAT_VERSION : FORMAT_LEGACY, timeZone); ? FORMAT_VERSION : FORMAT_LEGACY, timeZone, flags); } private HistoryPrinter(int formatVersion, TimeZone timeZone) { private HistoryPrinter(int formatVersion, TimeZone timeZone, int flags) { mFormatVersion = formatVersion; mPerformanceBaseline = (flags & DUMP_DEBUG_PERF_BASELINE) != 0; if (mPerformanceBaseline) { mHistoryItemTimestampFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US); mHistoryItemTimestampFormat.getCalendar().setTimeZone(timeZone); mHistoryLogTimeFormatter = null; } else { mHistoryItemTimestampFormat = null; mHistoryLogTimeFormatter = new HistoryLogTimeFormatter(timeZone); } mCurrentTimeEventTimeFormat.getCalendar().setTimeZone(timeZone); } Loading Loading @@ -7019,7 +7033,8 @@ public abstract class BatteryStats { @SuppressWarnings("JavaUtilDate") private String printNextItem(HistoryItem rec, long baseTime, boolean checkin, boolean verbose) { StringBuilder item = new StringBuilder(); StringBuilder item = mStringBuilder; item.setLength(0); if (!checkin) { item.append(" "); if (mFormatVersion == FORMAT_LEGACY) { Loading @@ -7029,8 +7044,13 @@ public abstract class BatteryStats { item.append(rec.numReadInts); item.append(") "); } else { if (mPerformanceBaseline) { mDate.setTime(rec.currentTime); item.append(mHistoryItemTimestampFormat.format(mDate)).append(' '); } else { mHistoryLogTimeFormatter.append(item, rec.currentTime); item.append(' '); } } } else { item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(','); Loading Loading @@ -7399,6 +7419,64 @@ public abstract class BatteryStats { sb.append(":"); sb.append(stime); } /** * In essence, this is a wrapper over SimpleDateFormat that takes advantage * of the fact that events in battery history are closely spaced, which allows it * to reuse the results of the most expensive formatting work. */ private static class HistoryLogTimeFormatter { private static final long HOUR_MILLIS = 3600000L; private static final long MINUTE_MILLIS = 60000L; private final SimpleDateFormat mDateFormat = new SimpleDateFormat("MM-dd HH:", Locale.US); private final Date mDate = new Date(); private final long mTimeZoneOffset; private long mCachedHour; private String mCachedHourFormatted; private HistoryLogTimeFormatter(TimeZone timeZone) { mTimeZoneOffset = timeZone.getRawOffset(); mDateFormat.getCalendar().setTimeZone(timeZone); } /* Appends timestampMs formatted as "MM-dd HH:mm:ss.SSS" */ void append(StringBuilder sb, long timestampMs) { long localTime = timestampMs + mTimeZoneOffset; long hour = localTime / HOUR_MILLIS; if (hour != mCachedHour) { mDate.setTime(timestampMs); mCachedHourFormatted = mDateFormat.format(mDate); mCachedHour = hour; } sb.append(mCachedHourFormatted); long remainder = localTime % HOUR_MILLIS; long minutes = remainder / MINUTE_MILLIS; if (minutes < 10) { sb.append('0'); } sb.append(minutes).append(':'); remainder = remainder % MINUTE_MILLIS; long seconds = remainder / 1000; if (seconds < 10) { sb.append('0'); } sb.append(seconds).append('.'); long millis = remainder % 1000; if (millis < 100) { sb.append('0'); if (millis < 10) { sb.append('0'); } } sb.append(millis); } } } private void printSizeValue(PrintWriter pw, long size) { Loading Loading @@ -7584,9 +7662,10 @@ public abstract class BatteryStats { public static final int DUMP_INCLUDE_HISTORY = 1<<4; public static final int DUMP_VERBOSE = 1<<5; public static final int DUMP_DEVICE_WIFI_ONLY = 1<<6; public static final int DUMP_DEBUG_PERF_BASELINE = 1 << 7; private void dumpHistory(PrintWriter pw, int flags, long histStart, boolean checkin) { final HistoryPrinter hprinter = new HistoryPrinter(); final HistoryPrinter hprinter = new HistoryPrinter(flags); synchronized (this) { if (!checkin) { final long historyTotalSize = getHistoryTotalSize(); Loading Loading @@ -8522,7 +8601,7 @@ public abstract class BatteryStats { } // History data (HISTORY_DATA) final HistoryPrinter hprinter = new HistoryPrinter(); final HistoryPrinter hprinter = new HistoryPrinter(flags); long lastTime = -1; long baseTime = -1; boolean printed = false; Loading services/core/java/com/android/server/am/BatteryStatsService.java +8 −0 Original line number Diff line number Diff line Loading @@ -3368,6 +3368,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub return; } else if ("-a".equals(arg)) { flags |= BatteryStats.DUMP_VERBOSE; } else if ("--debug".equals(arg)) { i++; if (i >= args.length) { pw.println("Missing time argument for --flags HEX"); dumpHelp(pw); return; } flags |= ParseUtils.parseIntWithBase(args[i], 16, 0); } else if (arg.length() > 0 && arg.charAt(0) == '-'){ pw.println("Unknown option: " + arg); dumpHelp(pw); Loading services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +38 −1 Original line number Diff line number Diff line Loading @@ -67,10 +67,14 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.TimeUnit; /** * Test BatteryStatsHistory. Loading Loading @@ -141,7 +145,7 @@ public class BatteryStatsHistoryTest { mEventLogger); mHistory.forceRecordAllHistory(); mHistory.startRecordingHistory(mClock.realtime, mClock.uptime, false); mHistoryPrinter = new BatteryStats.HistoryPrinter(TimeZone.getTimeZone("GMT")); mHistoryPrinter = new BatteryStats.HistoryPrinter(TimeZone.getTimeZone("GMT"), 0); } @Test Loading Loading @@ -865,6 +869,39 @@ public class BatteryStatsHistoryTest { return events; } @Test public void historyLogTimeFormatter() { historyLogTimeFormatting("GMT"); historyLogTimeFormatting("PST"); historyLogTimeFormatting("NST"); // UTC−03:30 historyLogTimeFormatting("NPT"); // UTC+05:45 } private void historyLogTimeFormatting(String timeZoneId) { TimeZone timeZone = TimeZone.getTimeZone(timeZoneId); BatteryStats.HistoryPrinter printer = new BatteryStats.HistoryPrinter(timeZone, 0); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US); simpleDateFormat.getCalendar().setTimeZone(timeZone); Date date = new Date(); HistoryItem item = new HistoryItem(); long base = 1738746000000L; for (long offset = 1; offset < TimeUnit.DAYS.toMillis(365 * 100); offset *= 7) { item.currentTime = base + offset; date.setTime(item.currentTime); String expected = simpleDateFormat.format(date); StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); printer.printNextItem(writer, item, 0, false, false); writer.flush(); String actual = sw.toString().trim().substring(0, "MM-dd HH:mm:ss.SSS".length()); assertThat(actual).isEqualTo(expected); } } private static void awaitCompletion() { ConditionVariable done = new ConditionVariable(); BackgroundThread.getHandler().post(done::open); Loading Loading
core/java/android/os/BatteryStats.java +91 −12 Original line number Diff line number Diff line Loading @@ -6948,8 +6948,9 @@ public abstract class BatteryStats { // This constant MUST be incremented whenever the history dump format changes. private static final int FORMAT_VERSION = 2; private final SimpleDateFormat mHistoryItemTimestampFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US); private final boolean mPerformanceBaseline; private final HistoryLogTimeFormatter mHistoryLogTimeFormatter; private final SimpleDateFormat mHistoryItemTimestampFormat; private final SimpleDateFormat mCurrentTimeEventTimeFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US); Loading @@ -6960,6 +6961,7 @@ public abstract class BatteryStats { private final int mFormatVersion; private final StringBuilder mStringBuilder = new StringBuilder(); int oldState = 0; int oldState2 = 0; int oldLevel = -1; Loading @@ -6974,18 +6976,30 @@ public abstract class BatteryStats { long lastTime = -1; public HistoryPrinter() { this(TimeZone.getDefault()); this(0); } public HistoryPrinter(TimeZone timeZone) { public HistoryPrinter(int flags) { this(TimeZone.getDefault(), flags); } public HistoryPrinter(TimeZone timeZone, int flags) { this(com.android.server.power.optimization.Flags .extendedBatteryHistoryContinuousCollectionEnabled() ? FORMAT_VERSION : FORMAT_LEGACY, timeZone); ? FORMAT_VERSION : FORMAT_LEGACY, timeZone, flags); } private HistoryPrinter(int formatVersion, TimeZone timeZone) { private HistoryPrinter(int formatVersion, TimeZone timeZone, int flags) { mFormatVersion = formatVersion; mPerformanceBaseline = (flags & DUMP_DEBUG_PERF_BASELINE) != 0; if (mPerformanceBaseline) { mHistoryItemTimestampFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US); mHistoryItemTimestampFormat.getCalendar().setTimeZone(timeZone); mHistoryLogTimeFormatter = null; } else { mHistoryItemTimestampFormat = null; mHistoryLogTimeFormatter = new HistoryLogTimeFormatter(timeZone); } mCurrentTimeEventTimeFormat.getCalendar().setTimeZone(timeZone); } Loading Loading @@ -7019,7 +7033,8 @@ public abstract class BatteryStats { @SuppressWarnings("JavaUtilDate") private String printNextItem(HistoryItem rec, long baseTime, boolean checkin, boolean verbose) { StringBuilder item = new StringBuilder(); StringBuilder item = mStringBuilder; item.setLength(0); if (!checkin) { item.append(" "); if (mFormatVersion == FORMAT_LEGACY) { Loading @@ -7029,8 +7044,13 @@ public abstract class BatteryStats { item.append(rec.numReadInts); item.append(") "); } else { if (mPerformanceBaseline) { mDate.setTime(rec.currentTime); item.append(mHistoryItemTimestampFormat.format(mDate)).append(' '); } else { mHistoryLogTimeFormatter.append(item, rec.currentTime); item.append(' '); } } } else { item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(','); Loading Loading @@ -7399,6 +7419,64 @@ public abstract class BatteryStats { sb.append(":"); sb.append(stime); } /** * In essence, this is a wrapper over SimpleDateFormat that takes advantage * of the fact that events in battery history are closely spaced, which allows it * to reuse the results of the most expensive formatting work. */ private static class HistoryLogTimeFormatter { private static final long HOUR_MILLIS = 3600000L; private static final long MINUTE_MILLIS = 60000L; private final SimpleDateFormat mDateFormat = new SimpleDateFormat("MM-dd HH:", Locale.US); private final Date mDate = new Date(); private final long mTimeZoneOffset; private long mCachedHour; private String mCachedHourFormatted; private HistoryLogTimeFormatter(TimeZone timeZone) { mTimeZoneOffset = timeZone.getRawOffset(); mDateFormat.getCalendar().setTimeZone(timeZone); } /* Appends timestampMs formatted as "MM-dd HH:mm:ss.SSS" */ void append(StringBuilder sb, long timestampMs) { long localTime = timestampMs + mTimeZoneOffset; long hour = localTime / HOUR_MILLIS; if (hour != mCachedHour) { mDate.setTime(timestampMs); mCachedHourFormatted = mDateFormat.format(mDate); mCachedHour = hour; } sb.append(mCachedHourFormatted); long remainder = localTime % HOUR_MILLIS; long minutes = remainder / MINUTE_MILLIS; if (minutes < 10) { sb.append('0'); } sb.append(minutes).append(':'); remainder = remainder % MINUTE_MILLIS; long seconds = remainder / 1000; if (seconds < 10) { sb.append('0'); } sb.append(seconds).append('.'); long millis = remainder % 1000; if (millis < 100) { sb.append('0'); if (millis < 10) { sb.append('0'); } } sb.append(millis); } } } private void printSizeValue(PrintWriter pw, long size) { Loading Loading @@ -7584,9 +7662,10 @@ public abstract class BatteryStats { public static final int DUMP_INCLUDE_HISTORY = 1<<4; public static final int DUMP_VERBOSE = 1<<5; public static final int DUMP_DEVICE_WIFI_ONLY = 1<<6; public static final int DUMP_DEBUG_PERF_BASELINE = 1 << 7; private void dumpHistory(PrintWriter pw, int flags, long histStart, boolean checkin) { final HistoryPrinter hprinter = new HistoryPrinter(); final HistoryPrinter hprinter = new HistoryPrinter(flags); synchronized (this) { if (!checkin) { final long historyTotalSize = getHistoryTotalSize(); Loading Loading @@ -8522,7 +8601,7 @@ public abstract class BatteryStats { } // History data (HISTORY_DATA) final HistoryPrinter hprinter = new HistoryPrinter(); final HistoryPrinter hprinter = new HistoryPrinter(flags); long lastTime = -1; long baseTime = -1; boolean printed = false; Loading
services/core/java/com/android/server/am/BatteryStatsService.java +8 −0 Original line number Diff line number Diff line Loading @@ -3368,6 +3368,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub return; } else if ("-a".equals(arg)) { flags |= BatteryStats.DUMP_VERBOSE; } else if ("--debug".equals(arg)) { i++; if (i >= args.length) { pw.println("Missing time argument for --flags HEX"); dumpHelp(pw); return; } flags |= ParseUtils.parseIntWithBase(args[i], 16, 0); } else if (arg.length() > 0 && arg.charAt(0) == '-'){ pw.println("Unknown option: " + arg); dumpHelp(pw); Loading
services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +38 −1 Original line number Diff line number Diff line Loading @@ -67,10 +67,14 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.TimeUnit; /** * Test BatteryStatsHistory. Loading Loading @@ -141,7 +145,7 @@ public class BatteryStatsHistoryTest { mEventLogger); mHistory.forceRecordAllHistory(); mHistory.startRecordingHistory(mClock.realtime, mClock.uptime, false); mHistoryPrinter = new BatteryStats.HistoryPrinter(TimeZone.getTimeZone("GMT")); mHistoryPrinter = new BatteryStats.HistoryPrinter(TimeZone.getTimeZone("GMT"), 0); } @Test Loading Loading @@ -865,6 +869,39 @@ public class BatteryStatsHistoryTest { return events; } @Test public void historyLogTimeFormatter() { historyLogTimeFormatting("GMT"); historyLogTimeFormatting("PST"); historyLogTimeFormatting("NST"); // UTC−03:30 historyLogTimeFormatting("NPT"); // UTC+05:45 } private void historyLogTimeFormatting(String timeZoneId) { TimeZone timeZone = TimeZone.getTimeZone(timeZoneId); BatteryStats.HistoryPrinter printer = new BatteryStats.HistoryPrinter(timeZone, 0); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US); simpleDateFormat.getCalendar().setTimeZone(timeZone); Date date = new Date(); HistoryItem item = new HistoryItem(); long base = 1738746000000L; for (long offset = 1; offset < TimeUnit.DAYS.toMillis(365 * 100); offset *= 7) { item.currentTime = base + offset; date.setTime(item.currentTime); String expected = simpleDateFormat.format(date); StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); printer.printNextItem(writer, item, 0, false, false); writer.flush(); String actual = sw.toString().trim().substring(0, "MM-dd HH:mm:ss.SSS".length()); assertThat(actual).isEqualTo(expected); } } private static void awaitCompletion() { ConditionVariable done = new ConditionVariable(); BackgroundThread.getHandler().post(done::open); Loading