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

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

Capture wallclock time in aggregated power stats

Bug: 297274264
Test: atest PowerStatsTests

Change-Id: Ic24c555e7843578e66fdb2d0d6800bc0ad399bc2
parent 98d0f477
Loading
Loading
Loading
Loading
+68 −13
Original line number Diff line number Diff line
@@ -16,16 +16,21 @@

package com.android.server.power.stats;

import android.annotation.CurrentTimeMillisLong;
import android.annotation.DurationMillisLong;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.TimeUtils;

import com.android.internal.os.PowerStats;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
@@ -33,10 +38,16 @@ import java.util.Set;
 * etc) covering a specific period of power usage history.
 */
class AggregatedPowerStats {
    private static final String TAG = "AggregatedPowerStats";
    private static final int MAX_CLOCK_UPDATES = 100;
    private final PowerComponentAggregatedPowerStats[] mPowerComponentStats;

    // See MonotonicClock
    private long mStartTime;
    static class ClockUpdate {
        public long monotonicTime;
        @CurrentTimeMillisLong public long currentTime;
    }

    private final List<ClockUpdate> mClockUpdates = new ArrayList<>();

    @DurationMillisLong
    private long mDurationMs;
@@ -46,17 +57,39 @@ class AggregatedPowerStats {
    }

    /**
     * @param startTime monotonic time
     * Records a mapping of monotonic time to wall-clock time. Since wall-clock time can change,
     * there may be multiple clock updates in one set of aggregated stats.
     *
     * @param monotonicTime monotonic time in milliseconds, see
     * {@link com.android.internal.os.MonotonicClock}
     * @param currentTime   current time in milliseconds, see {@link System#currentTimeMillis()}
     */
    void setStartTime(long startTime) {
        mStartTime = startTime;
    void addClockUpdate(long monotonicTime, @CurrentTimeMillisLong long currentTime) {
        ClockUpdate clockUpdate = new ClockUpdate();
        clockUpdate.monotonicTime = monotonicTime;
        clockUpdate.currentTime = currentTime;
        if (mClockUpdates.size() < MAX_CLOCK_UPDATES) {
            mClockUpdates.add(clockUpdate);
        } else {
            Slog.i(TAG, "Too many clock updates. Replacing the previous update with "
                        + DateFormat.format("yyyy-MM-dd-HH-mm-ss", currentTime));
            mClockUpdates.set(mClockUpdates.size() - 1, clockUpdate);
        }
    }

    /**
     * Start time according to {@link com.android.internal.os.MonotonicClock}
     */
    public long getStartTime() {
        return mStartTime;
    long getStartTime() {
        if (mClockUpdates.isEmpty()) {
            return 0;
        } else {
            return mClockUpdates.get(0).monotonicTime;
        }
    }

    List<ClockUpdate> getClockUpdates() {
        return mClockUpdates;
    }

    void setDuration(long durationMs) {
@@ -110,7 +143,7 @@ class AggregatedPowerStats {
    }

    void reset() {
        mStartTime = 0;
        mClockUpdates.clear();
        mDurationMs = 0;
        for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
            stats.reset();
@@ -119,11 +152,33 @@ class AggregatedPowerStats {

    void dump(PrintWriter pw) {
        IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
        ipw.print("Start time: ");
        ipw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", mStartTime));
        ipw.print(" duration: ");
        ipw.print(mDurationMs);
        ipw.println();
        StringBuilder sb = new StringBuilder();
        long baseTime = 0;
        for (int i = 0; i < mClockUpdates.size(); i++) {
            ClockUpdate clockUpdate = mClockUpdates.get(i);
            sb.setLength(0);
            if (i == 0) {
                baseTime = clockUpdate.monotonicTime;
                sb.append("Start time: ")
                        .append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", clockUpdate.currentTime))
                        .append(" (")
                        .append(baseTime)
                        .append(") duration: ")
                        .append(mDurationMs);
                ipw.println(sb);
            } else {
                sb.setLength(0);
                sb.append("Clock update:  ");
                TimeUtils.formatDuration(
                        clockUpdate.monotonicTime - baseTime, sb,
                        TimeUtils.HUNDRED_DAY_FIELD_LEN + 3);
                sb.append(" ").append(
                        DateFormat.format("yyyy-MM-dd-HH-mm-ss", clockUpdate.currentTime));
                ipw.increaseIndent();
                ipw.println(sb);
                ipw.decreaseIndent();
            }
        }

        ipw.println("Device");
        ipw.increaseIndent();
+5 −2
Original line number Diff line number Diff line
@@ -96,8 +96,11 @@ class PowerStatsAggregator {
                BatteryStats.HistoryItem item = iterator.next();

                if (baseTime < 0) {
                    mStats.setStartTime(item.time);
                    mStats.addClockUpdate(item.time, item.currentTime);
                    baseTime = item.time;
                } else if (item.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME
                           || item.cmd == BatteryStats.HistoryItem.CMD_RESET) {
                    mStats.addClockUpdate(item.time, item.currentTime);
                }

                lastTime = item.time;
@@ -128,7 +131,7 @@ class PowerStatsAggregator {
                        mStats.setDuration(lastTime - baseTime);
                        consumer.accept(mStats);
                        mStats.reset();
                        mStats.setStartTime(item.time);
                        mStats.addClockUpdate(item.time, item.currentTime);
                        baseTime = lastTime = item.time;
                    }
                    mStats.addPowerStats(item.powerStats, item.time);
+32 −1
Original line number Diff line number Diff line
@@ -24,7 +24,9 @@ import static org.mockito.Mockito.mock;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.PersistableBundle;
import android.text.format.DateFormat;

import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

@@ -37,6 +39,9 @@ import org.junit.Test;
import org.junit.runner.RunWith;

import java.text.ParseException;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;

@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -71,6 +76,8 @@ public class PowerStatsAggregatorTest {

    @Test
    public void stateUpdates() {
        mClock.currentTime = 1222156800000L;    // An important date in world history

        mHistory.forceRecordAllHistory();
        mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
        mHistory.recordStateStartEvent(mClock.realtime, mClock.uptime,
@@ -97,7 +104,12 @@ public class PowerStatsAggregatorTest {
        mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
                BatteryConsumer.PROCESS_STATE_BACKGROUND);

        advance(3000);
        advance(1000);

        mClock.currentTime += 60 * 60 * 1000;       // one hour
        mHistory.recordCurrentTimeChange(mClock.realtime, mClock.uptime, mClock.currentTime);

        advance(2000);

        powerStats.stats = new long[]{20000};
        powerStats.uidStats.put(TEST_UID, new long[]{4444});
@@ -106,6 +118,18 @@ public class PowerStatsAggregatorTest {
        mAggregator.aggregateBatteryStats(0, 0, stats -> {
            assertThat(mAggregatedStatsCount++).isEqualTo(0);
            assertThat(stats.getStartTime()).isEqualTo(START_TIME);

            List<AggregatedPowerStats.ClockUpdate> clockUpdates = stats.getClockUpdates();
            assertThat(clockUpdates).hasSize(2);

            AggregatedPowerStats.ClockUpdate clockUpdate0 = clockUpdates.get(0);
            assertThat(clockUpdate0.monotonicTime).isEqualTo(1234);
            assertThat(formatDateTime(clockUpdate0.currentTime)).isEqualTo("2008-09-23 08:00:00");

            AggregatedPowerStats.ClockUpdate clockUpdate1 = clockUpdates.get(1);
            assertThat(clockUpdate1.monotonicTime).isEqualTo(1234 + 3000);
            assertThat(formatDateTime(clockUpdate1.currentTime)).isEqualTo("2008-09-23 09:00:03");

            assertThat(stats.getDuration()).isEqualTo(5000);

            long[] values = new long[1];
@@ -148,6 +172,13 @@ public class PowerStatsAggregatorTest {
        });
    }

    @NonNull
    private static CharSequence formatDateTime(long timeInMillis) {
        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        cal.setTimeInMillis(timeInMillis);
        return DateFormat.format("yyyy-MM-dd hh:mm:ss", cal);
    }

    @Test
    public void incompatiblePowerStats() {
        mHistory.forceRecordAllHistory();