Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f973f2e8 authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Use optimized date formatting in battery history dump

Bug: 403385187
Test: atest PowerStatsTests; atest PowerStatsTestsRavenwood
Flag: EXEMPT bugfix

Change-Id: I07cbb5e55f040f6568b5ae4ccf6a71e89feec033
parent 2c11a679
Loading
Loading
Loading
Loading
+91 −12
Original line number Diff line number Diff line
@@ -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);

@@ -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;
@@ -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);
        }

@@ -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) {
@@ -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(',');
@@ -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) {
@@ -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();
@@ -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;
+8 −0
Original line number Diff line number Diff line
@@ -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);
+38 −1
Original line number Diff line number Diff line
@@ -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.
@@ -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
@@ -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);