diff options
3 files changed, 105 insertions, 16 deletions
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java index 65d7f1cfc60f..288912a2b89f 100644 --- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java +++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java @@ -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(); diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java index 5e47da3992a5..cfb081dca70f 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java @@ -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); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java index 45f30a34260d..5bb38858ab7c 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java @@ -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(); |