Loading core/java/android/os/BatteryStats.java +76 −26 Original line number Diff line number Diff line Loading @@ -69,16 +69,19 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Formatter; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TimeZone; /** * A class providing access to battery usage statistics, including information on Loading Loading @@ -1868,6 +1871,11 @@ public abstract class BatteryStats { @UnsupportedAppUsage public long time; // Wall clock time of the event, GMT. Unlike `time`, this timestamp is affected // by changes in the clock setting. When the wall clock is adjusted, BatteryHistory // records an event of type `CMD_CURRENT_TIME` or `CMD_RESET`. public long currentTime; @UnsupportedAppUsage public static final byte CMD_UPDATE = 0; // These can be written as deltas public static final byte CMD_NULL = -1; Loading Loading @@ -2108,9 +2116,6 @@ public abstract class BatteryStats { public int eventCode; public HistoryTag eventTag; // Only set for CMD_CURRENT_TIME or CMD_RESET, as per System.currentTimeMillis(). public long currentTime; // Meta-data when reading. public int numReadInts; Loading Loading @@ -6926,6 +6931,23 @@ public abstract class BatteryStats { } public static class HistoryPrinter { private static final int FORMAT_LEGACY = 1; // 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 SimpleDateFormat mCurrentTimeEventTimeFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US); // This API is error prone, but we are making an exception here to avoid excessive // object allocations. @SuppressWarnings("JavaUtilDate") private final Date mDate = new Date(); private final int mFormatVersion; int oldState = 0; int oldState2 = 0; int oldLevel = -1; Loading @@ -6939,6 +6961,22 @@ public abstract class BatteryStats { double oldWifiRailChargeMah = -1; long lastTime = -1; public HistoryPrinter() { this(TimeZone.getDefault()); } public HistoryPrinter(TimeZone timeZone) { this(com.android.server.power.optimization.Flags .extendedBatteryHistoryContinuousCollectionEnabled() ? FORMAT_VERSION : FORMAT_LEGACY, timeZone); } private HistoryPrinter(int formatVersion, TimeZone timeZone) { mFormatVersion = formatVersion; mHistoryItemTimestampFormat.getCalendar().setTimeZone(timeZone); mCurrentTimeEventTimeFormat.getCalendar().setTimeZone(timeZone); } void reset() { oldState = oldState2 = 0; oldLevel = -1; Loading Loading @@ -6966,16 +7004,22 @@ public abstract class BatteryStats { } } @SuppressWarnings("JavaUtilDate") private String printNextItem(HistoryItem rec, long baseTime, boolean checkin, boolean verbose) { StringBuilder item = new StringBuilder(); if (!checkin) { item.append(" "); if (mFormatVersion == FORMAT_LEGACY) { TimeUtils.formatDuration( rec.time - baseTime, item, TimeUtils.HUNDRED_DAY_FIELD_LEN); item.append(" ("); item.append(rec.numReadInts); item.append(") "); } else { mDate.setTime(rec.currentTime); item.append(mHistoryItemTimestampFormat.format(mDate)).append(' '); } } else { item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(','); item.append(HISTORY_DATA); item.append(','); Loading Loading @@ -7007,8 +7051,8 @@ public abstract class BatteryStats { item.append("\n"); } else { item.append(" "); item.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", rec.currentTime).toString()); mDate.setTime(rec.currentTime); item.append(mCurrentTimeEventTimeFormat.format(mDate)); item.append("\n"); } } else if (rec.cmd == HistoryItem.CMD_SHUTDOWN) { Loading Loading @@ -7529,11 +7573,31 @@ public abstract class BatteryStats { public static final int DUMP_DEVICE_WIFI_ONLY = 1<<6; private void dumpHistory(PrintWriter pw, int flags, long histStart, boolean checkin) { final HistoryPrinter hprinter = new HistoryPrinter(); synchronized (this) { if (!checkin) { final long historyTotalSize = getHistoryTotalSize(); final long historyUsedSize = getHistoryUsedSize(); pw.print("Battery History"); if (hprinter.mFormatVersion != HistoryPrinter.FORMAT_LEGACY) { pw.print(" [Format: " + hprinter.mFormatVersion + "]"); } pw.print(" ("); pw.print((100 * historyUsedSize) / historyTotalSize); pw.print("% used, "); printSizeValue(pw, historyUsedSize); pw.print(" used of "); printSizeValue(pw, historyTotalSize); pw.print(", "); pw.print(getHistoryStringPoolSize()); pw.print(" strings using "); printSizeValue(pw, getHistoryStringPoolBytes()); pw.println("):"); } else { dumpHistoryTagPoolLocked(pw, checkin); } } final HistoryPrinter hprinter = new HistoryPrinter(); long lastTime = -1; long baseTime = -1; boolean printed = false; Loading Loading @@ -7645,20 +7709,6 @@ public abstract class BatteryStats { pw.print("\""); pw.println(); } } else { final long historyTotalSize = getHistoryTotalSize(); final long historyUsedSize = getHistoryUsedSize(); pw.print("Battery History ("); pw.print((100 * historyUsedSize) / historyTotalSize); pw.print("% used, "); printSizeValue(pw, historyUsedSize); pw.print(" used of "); printSizeValue(pw, historyTotalSize); pw.print(", "); pw.print(getHistoryStringPoolSize()); pw.print(" strings using "); printSizeValue(pw, getHistoryStringPoolBytes()); pw.println("):"); } } Loading core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +11 −8 Original line number Diff line number Diff line Loading @@ -45,6 +45,8 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor private boolean mNextItemReady; private boolean mTimeInitialized; private boolean mClosed; private long mBaseMonotonicTime; private long mBaseTimeUtc; public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs, long endTimeMs) { Loading Loading @@ -84,24 +86,25 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor } if (!mTimeInitialized) { mHistoryItem.time = mBatteryStatsHistory.getHistoryBufferStartTime(p); mBaseMonotonicTime = mBatteryStatsHistory.getHistoryBufferStartTime(p); mHistoryItem.time = mBaseMonotonicTime; mTimeInitialized = true; } final long lastMonotonicTimeMs = mHistoryItem.time; final long lastWalltimeMs = mHistoryItem.currentTime; try { readHistoryDelta(p, mHistoryItem); } catch (Throwable t) { Slog.wtf(TAG, "Corrupted battery history", t); break; } if (mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME && mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_RESET && lastWalltimeMs != 0) { mHistoryItem.currentTime = lastWalltimeMs + (mHistoryItem.time - lastMonotonicTimeMs); if (mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME || mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_RESET) { mBaseTimeUtc = mHistoryItem.currentTime - (mHistoryItem.time - mBaseMonotonicTime); } mHistoryItem.currentTime = mBaseTimeUtc + (mHistoryItem.time - mBaseMonotonicTime); if (mEndTimeMs != 0 && mHistoryItem.time >= mEndTimeMs) { break; } Loading services/core/java/com/android/server/power/stats/flags.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -87,3 +87,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "extended_battery_history_continuous_collection_enabled" namespace: "backstage_power" description: "Disable automatic reset of battery stats history on full charge" bug: "381940953" metadata { purpose: PURPOSE_BUGFIX } } services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +45 −28 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ import android.os.Parcel; import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.telephony.NetworkRegistrationInfo; import android.util.AtomicFile; import android.util.Log; Loading @@ -46,6 +48,7 @@ import com.android.internal.os.MonotonicClock; import com.android.internal.os.PowerStats; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; Loading @@ -61,14 +64,22 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.TimeZone; /** * Test BatteryStatsHistory. */ @RunWith(AndroidJUnit4.class) @EnableFlags({com.android.server.power.optimization.Flags .FLAG_EXTENDED_BATTERY_HISTORY_CONTINUOUS_COLLECTION_ENABLED}) public class BatteryStatsHistoryTest { private static final String TAG = "BatteryStatsHistoryTest"; private static final int MAX_HISTORY_BUFFER_SIZE = 1024; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private final Parcel mHistoryBuffer = Parcel.obtain(); private File mSystemDir; private File mHistoryDir; Loading Loading @@ -98,15 +109,18 @@ public class BatteryStatsHistoryTest { mHistoryDir.delete(); mClock.realtime = 123; mClock.currentTime = 1743645660000L; // 2025-04-03, 2:01:00 AM mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32768, MAX_HISTORY_BUFFER_SIZE, mStepDetailsCalculator, mClock, mMonotonicClock, mTracer, mEventLogger); mHistory.forceRecordAllHistory(); mHistory.startRecordingHistory(mClock.realtime, mClock.uptime, false); when(mStepDetailsCalculator.getHistoryStepDetails()) .thenReturn(new BatteryStats.HistoryStepDetails()); mHistoryPrinter = new BatteryStats.HistoryPrinter(); mHistoryPrinter = new BatteryStats.HistoryPrinter(TimeZone.getTimeZone("GMT")); } @Test Loading Loading @@ -145,8 +159,6 @@ public class BatteryStatsHistoryTest { @Test public void testAtraceExcludedState() { mHistory.forceRecordAllHistory(); Mockito.when(mTracer.tracingEnabled()).thenReturn(true); mHistory.recordStateStartEvent(mClock.elapsedRealtime(), Loading Loading @@ -354,8 +366,6 @@ public class BatteryStatsHistoryTest { } private void prepareMultiFileHistory() { mHistory.forceRecordAllHistory(); mClock.realtime = 1000; mClock.uptime = 1000; mHistory.recordEvent(mClock.realtime, mClock.uptime, Loading Loading @@ -428,7 +438,8 @@ public class BatteryStatsHistoryTest { powerStats.uidStats.put(300, new long[]{400, 500}); powerStats.uidStats.put(600, new long[]{700, 800}); mHistory.recordPowerStats(200, 200, powerStats); mClock.advance(200); mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats); BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED); BatteryStats.HistoryItem item; Loading @@ -437,7 +448,7 @@ public class BatteryStatsHistoryTest { assertThat(item = iterator.next()).isNotNull(); String dump = toString(item, /* checkin */ false); assertThat(dump).contains("+200ms"); assertThat(dump).contains("04-03 02:01:00.200"); assertThat(dump).contains("duration=100"); assertThat(dump).contains("foo=[200]"); assertThat(dump).contains("300: [400, 500]"); Loading @@ -446,49 +457,49 @@ public class BatteryStatsHistoryTest { @Test public void testNrState_dump() { mHistory.forceRecordAllHistory(); mHistory.startRecordingHistory(0, 0, /* reset */ true); mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80, 1234); mHistory.recordNrStateChangeEvent(200, 200, mClock.advance(200); mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime, NetworkRegistrationInfo.NR_STATE_RESTRICTED); mHistory.recordNrStateChangeEvent(300, 300, mClock.advance(100); mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime, NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED); mHistory.recordNrStateChangeEvent(400, 400, mClock.advance(100); mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime, NetworkRegistrationInfo.NR_STATE_CONNECTED); mHistory.recordNrStateChangeEvent(500, 500, mClock.advance(100); mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime, NetworkRegistrationInfo.NR_STATE_NONE); BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED); BatteryStats.HistoryItem item = new BatteryStats.HistoryItem(); BatteryStats.HistoryItem item; assertThat(item = iterator.next()).isNotNull(); // First item contains current time only assertThat(item = iterator.next()).isNotNull(); String dump = toString(item, /* checkin */ false); assertThat(dump).contains("+200ms"); assertThat(dump).contains("04-03 02:01:00.200"); assertThat(dump).contains("nr_state=restricted"); assertThat(item = iterator.next()).isNotNull(); dump = toString(item, /* checkin */ false); assertThat(dump).contains("+300ms"); assertThat(dump).contains("04-03 02:01:00.300"); assertThat(dump).contains("nr_state=not_restricted"); assertThat(item = iterator.next()).isNotNull(); dump = toString(item, /* checkin */ false); assertThat(dump).contains("+400ms"); assertThat(dump).contains("04-03 02:01:00.400"); assertThat(dump).contains("nr_state=connected"); assertThat(item = iterator.next()).isNotNull(); dump = toString(item, /* checkin */ false); assertThat(dump).contains("+500ms"); assertThat(dump).contains("04-03 02:01:00.500"); assertThat(dump).contains("nr_state=none"); } @Test public void testNrState_checkin() { mHistory.forceRecordAllHistory(); mHistory.startRecordingHistory(0, 0, /* reset */ true); mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80, 1234); Loading @@ -502,7 +513,7 @@ public class BatteryStatsHistoryTest { NetworkRegistrationInfo.NR_STATE_NONE); BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED); BatteryStats.HistoryItem item = new BatteryStats.HistoryItem(); BatteryStats.HistoryItem item; assertThat(item = iterator.next()).isNotNull(); // First item contains current time only assertThat(item = iterator.next()).isNotNull(); Loading Loading @@ -633,10 +644,17 @@ public class BatteryStatsHistoryTest { @Test public void recordProcStateChange() { mHistory.recordProcessStateChange(200, 200, 42, BatteryConsumer.PROCESS_STATE_BACKGROUND); mHistory.recordProcessStateChange(300, 300, 42, BatteryConsumer.PROCESS_STATE_FOREGROUND); mClock.advance(200); mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, 42, BatteryConsumer.PROCESS_STATE_BACKGROUND); mClock.advance(100); mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, 42, BatteryConsumer.PROCESS_STATE_FOREGROUND); mClock.advance(100); // Large UID, > 0xFFFFFF mHistory.recordProcessStateChange(400, 400, mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, UserHandle.getUid(777, Process.LAST_ISOLATED_UID), BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE); Loading @@ -647,17 +665,17 @@ public class BatteryStatsHistoryTest { assertThat(item = iterator.next()).isNotNull(); String dump = toString(item, /* checkin */ false); assertThat(dump).contains("+200ms"); assertThat(dump).contains("04-03 02:01:00.200"); assertThat(dump).contains("procstate: 42: bg"); assertThat(item = iterator.next()).isNotNull(); dump = toString(item, /* checkin */ false); assertThat(dump).contains("+300ms"); assertThat(dump).contains("04-03 02:01:00.300"); assertThat(dump).contains("procstate: 42: fg"); assertThat(item = iterator.next()).isNotNull(); dump = toString(item, /* checkin */ false); assertThat(dump).contains("+400ms"); assertThat(dump).contains("04-03 02:01:00.400"); assertThat(dump).contains("procstate: u777i999: fgs"); } Loading @@ -672,7 +690,6 @@ public class BatteryStatsHistoryTest { @Test public void getMonotonicHistorySize() { long lastHistorySize = mHistory.getMonotonicHistorySize(); mHistory.forceRecordAllHistory(); mClock.realtime = 1000; mClock.uptime = 1000; Loading services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java +9 −0 Original line number Diff line number Diff line Loading @@ -40,4 +40,13 @@ public class MockClock extends Clock { public long currentTimeMillis() { return currentTime; } /** * Advances the clock by the given number of milliseconds. */ public void advance(long milliseconds) { realtime += milliseconds; uptime += milliseconds; currentTime += milliseconds; } } Loading
core/java/android/os/BatteryStats.java +76 −26 Original line number Diff line number Diff line Loading @@ -69,16 +69,19 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Formatter; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TimeZone; /** * A class providing access to battery usage statistics, including information on Loading Loading @@ -1868,6 +1871,11 @@ public abstract class BatteryStats { @UnsupportedAppUsage public long time; // Wall clock time of the event, GMT. Unlike `time`, this timestamp is affected // by changes in the clock setting. When the wall clock is adjusted, BatteryHistory // records an event of type `CMD_CURRENT_TIME` or `CMD_RESET`. public long currentTime; @UnsupportedAppUsage public static final byte CMD_UPDATE = 0; // These can be written as deltas public static final byte CMD_NULL = -1; Loading Loading @@ -2108,9 +2116,6 @@ public abstract class BatteryStats { public int eventCode; public HistoryTag eventTag; // Only set for CMD_CURRENT_TIME or CMD_RESET, as per System.currentTimeMillis(). public long currentTime; // Meta-data when reading. public int numReadInts; Loading Loading @@ -6926,6 +6931,23 @@ public abstract class BatteryStats { } public static class HistoryPrinter { private static final int FORMAT_LEGACY = 1; // 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 SimpleDateFormat mCurrentTimeEventTimeFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US); // This API is error prone, but we are making an exception here to avoid excessive // object allocations. @SuppressWarnings("JavaUtilDate") private final Date mDate = new Date(); private final int mFormatVersion; int oldState = 0; int oldState2 = 0; int oldLevel = -1; Loading @@ -6939,6 +6961,22 @@ public abstract class BatteryStats { double oldWifiRailChargeMah = -1; long lastTime = -1; public HistoryPrinter() { this(TimeZone.getDefault()); } public HistoryPrinter(TimeZone timeZone) { this(com.android.server.power.optimization.Flags .extendedBatteryHistoryContinuousCollectionEnabled() ? FORMAT_VERSION : FORMAT_LEGACY, timeZone); } private HistoryPrinter(int formatVersion, TimeZone timeZone) { mFormatVersion = formatVersion; mHistoryItemTimestampFormat.getCalendar().setTimeZone(timeZone); mCurrentTimeEventTimeFormat.getCalendar().setTimeZone(timeZone); } void reset() { oldState = oldState2 = 0; oldLevel = -1; Loading Loading @@ -6966,16 +7004,22 @@ public abstract class BatteryStats { } } @SuppressWarnings("JavaUtilDate") private String printNextItem(HistoryItem rec, long baseTime, boolean checkin, boolean verbose) { StringBuilder item = new StringBuilder(); if (!checkin) { item.append(" "); if (mFormatVersion == FORMAT_LEGACY) { TimeUtils.formatDuration( rec.time - baseTime, item, TimeUtils.HUNDRED_DAY_FIELD_LEN); item.append(" ("); item.append(rec.numReadInts); item.append(") "); } else { mDate.setTime(rec.currentTime); item.append(mHistoryItemTimestampFormat.format(mDate)).append(' '); } } else { item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(','); item.append(HISTORY_DATA); item.append(','); Loading Loading @@ -7007,8 +7051,8 @@ public abstract class BatteryStats { item.append("\n"); } else { item.append(" "); item.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", rec.currentTime).toString()); mDate.setTime(rec.currentTime); item.append(mCurrentTimeEventTimeFormat.format(mDate)); item.append("\n"); } } else if (rec.cmd == HistoryItem.CMD_SHUTDOWN) { Loading Loading @@ -7529,11 +7573,31 @@ public abstract class BatteryStats { public static final int DUMP_DEVICE_WIFI_ONLY = 1<<6; private void dumpHistory(PrintWriter pw, int flags, long histStart, boolean checkin) { final HistoryPrinter hprinter = new HistoryPrinter(); synchronized (this) { if (!checkin) { final long historyTotalSize = getHistoryTotalSize(); final long historyUsedSize = getHistoryUsedSize(); pw.print("Battery History"); if (hprinter.mFormatVersion != HistoryPrinter.FORMAT_LEGACY) { pw.print(" [Format: " + hprinter.mFormatVersion + "]"); } pw.print(" ("); pw.print((100 * historyUsedSize) / historyTotalSize); pw.print("% used, "); printSizeValue(pw, historyUsedSize); pw.print(" used of "); printSizeValue(pw, historyTotalSize); pw.print(", "); pw.print(getHistoryStringPoolSize()); pw.print(" strings using "); printSizeValue(pw, getHistoryStringPoolBytes()); pw.println("):"); } else { dumpHistoryTagPoolLocked(pw, checkin); } } final HistoryPrinter hprinter = new HistoryPrinter(); long lastTime = -1; long baseTime = -1; boolean printed = false; Loading Loading @@ -7645,20 +7709,6 @@ public abstract class BatteryStats { pw.print("\""); pw.println(); } } else { final long historyTotalSize = getHistoryTotalSize(); final long historyUsedSize = getHistoryUsedSize(); pw.print("Battery History ("); pw.print((100 * historyUsedSize) / historyTotalSize); pw.print("% used, "); printSizeValue(pw, historyUsedSize); pw.print(" used of "); printSizeValue(pw, historyTotalSize); pw.print(", "); pw.print(getHistoryStringPoolSize()); pw.print(" strings using "); printSizeValue(pw, getHistoryStringPoolBytes()); pw.println("):"); } } Loading
core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +11 −8 Original line number Diff line number Diff line Loading @@ -45,6 +45,8 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor private boolean mNextItemReady; private boolean mTimeInitialized; private boolean mClosed; private long mBaseMonotonicTime; private long mBaseTimeUtc; public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs, long endTimeMs) { Loading Loading @@ -84,24 +86,25 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor } if (!mTimeInitialized) { mHistoryItem.time = mBatteryStatsHistory.getHistoryBufferStartTime(p); mBaseMonotonicTime = mBatteryStatsHistory.getHistoryBufferStartTime(p); mHistoryItem.time = mBaseMonotonicTime; mTimeInitialized = true; } final long lastMonotonicTimeMs = mHistoryItem.time; final long lastWalltimeMs = mHistoryItem.currentTime; try { readHistoryDelta(p, mHistoryItem); } catch (Throwable t) { Slog.wtf(TAG, "Corrupted battery history", t); break; } if (mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME && mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_RESET && lastWalltimeMs != 0) { mHistoryItem.currentTime = lastWalltimeMs + (mHistoryItem.time - lastMonotonicTimeMs); if (mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME || mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_RESET) { mBaseTimeUtc = mHistoryItem.currentTime - (mHistoryItem.time - mBaseMonotonicTime); } mHistoryItem.currentTime = mBaseTimeUtc + (mHistoryItem.time - mBaseMonotonicTime); if (mEndTimeMs != 0 && mHistoryItem.time >= mEndTimeMs) { break; } Loading
services/core/java/com/android/server/power/stats/flags.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -87,3 +87,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "extended_battery_history_continuous_collection_enabled" namespace: "backstage_power" description: "Disable automatic reset of battery stats history on full charge" bug: "381940953" metadata { purpose: PURPOSE_BUGFIX } }
services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +45 −28 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ import android.os.Parcel; import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.telephony.NetworkRegistrationInfo; import android.util.AtomicFile; import android.util.Log; Loading @@ -46,6 +48,7 @@ import com.android.internal.os.MonotonicClock; import com.android.internal.os.PowerStats; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; Loading @@ -61,14 +64,22 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.TimeZone; /** * Test BatteryStatsHistory. */ @RunWith(AndroidJUnit4.class) @EnableFlags({com.android.server.power.optimization.Flags .FLAG_EXTENDED_BATTERY_HISTORY_CONTINUOUS_COLLECTION_ENABLED}) public class BatteryStatsHistoryTest { private static final String TAG = "BatteryStatsHistoryTest"; private static final int MAX_HISTORY_BUFFER_SIZE = 1024; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private final Parcel mHistoryBuffer = Parcel.obtain(); private File mSystemDir; private File mHistoryDir; Loading Loading @@ -98,15 +109,18 @@ public class BatteryStatsHistoryTest { mHistoryDir.delete(); mClock.realtime = 123; mClock.currentTime = 1743645660000L; // 2025-04-03, 2:01:00 AM mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32768, MAX_HISTORY_BUFFER_SIZE, mStepDetailsCalculator, mClock, mMonotonicClock, mTracer, mEventLogger); mHistory.forceRecordAllHistory(); mHistory.startRecordingHistory(mClock.realtime, mClock.uptime, false); when(mStepDetailsCalculator.getHistoryStepDetails()) .thenReturn(new BatteryStats.HistoryStepDetails()); mHistoryPrinter = new BatteryStats.HistoryPrinter(); mHistoryPrinter = new BatteryStats.HistoryPrinter(TimeZone.getTimeZone("GMT")); } @Test Loading Loading @@ -145,8 +159,6 @@ public class BatteryStatsHistoryTest { @Test public void testAtraceExcludedState() { mHistory.forceRecordAllHistory(); Mockito.when(mTracer.tracingEnabled()).thenReturn(true); mHistory.recordStateStartEvent(mClock.elapsedRealtime(), Loading Loading @@ -354,8 +366,6 @@ public class BatteryStatsHistoryTest { } private void prepareMultiFileHistory() { mHistory.forceRecordAllHistory(); mClock.realtime = 1000; mClock.uptime = 1000; mHistory.recordEvent(mClock.realtime, mClock.uptime, Loading Loading @@ -428,7 +438,8 @@ public class BatteryStatsHistoryTest { powerStats.uidStats.put(300, new long[]{400, 500}); powerStats.uidStats.put(600, new long[]{700, 800}); mHistory.recordPowerStats(200, 200, powerStats); mClock.advance(200); mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats); BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED); BatteryStats.HistoryItem item; Loading @@ -437,7 +448,7 @@ public class BatteryStatsHistoryTest { assertThat(item = iterator.next()).isNotNull(); String dump = toString(item, /* checkin */ false); assertThat(dump).contains("+200ms"); assertThat(dump).contains("04-03 02:01:00.200"); assertThat(dump).contains("duration=100"); assertThat(dump).contains("foo=[200]"); assertThat(dump).contains("300: [400, 500]"); Loading @@ -446,49 +457,49 @@ public class BatteryStatsHistoryTest { @Test public void testNrState_dump() { mHistory.forceRecordAllHistory(); mHistory.startRecordingHistory(0, 0, /* reset */ true); mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80, 1234); mHistory.recordNrStateChangeEvent(200, 200, mClock.advance(200); mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime, NetworkRegistrationInfo.NR_STATE_RESTRICTED); mHistory.recordNrStateChangeEvent(300, 300, mClock.advance(100); mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime, NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED); mHistory.recordNrStateChangeEvent(400, 400, mClock.advance(100); mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime, NetworkRegistrationInfo.NR_STATE_CONNECTED); mHistory.recordNrStateChangeEvent(500, 500, mClock.advance(100); mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime, NetworkRegistrationInfo.NR_STATE_NONE); BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED); BatteryStats.HistoryItem item = new BatteryStats.HistoryItem(); BatteryStats.HistoryItem item; assertThat(item = iterator.next()).isNotNull(); // First item contains current time only assertThat(item = iterator.next()).isNotNull(); String dump = toString(item, /* checkin */ false); assertThat(dump).contains("+200ms"); assertThat(dump).contains("04-03 02:01:00.200"); assertThat(dump).contains("nr_state=restricted"); assertThat(item = iterator.next()).isNotNull(); dump = toString(item, /* checkin */ false); assertThat(dump).contains("+300ms"); assertThat(dump).contains("04-03 02:01:00.300"); assertThat(dump).contains("nr_state=not_restricted"); assertThat(item = iterator.next()).isNotNull(); dump = toString(item, /* checkin */ false); assertThat(dump).contains("+400ms"); assertThat(dump).contains("04-03 02:01:00.400"); assertThat(dump).contains("nr_state=connected"); assertThat(item = iterator.next()).isNotNull(); dump = toString(item, /* checkin */ false); assertThat(dump).contains("+500ms"); assertThat(dump).contains("04-03 02:01:00.500"); assertThat(dump).contains("nr_state=none"); } @Test public void testNrState_checkin() { mHistory.forceRecordAllHistory(); mHistory.startRecordingHistory(0, 0, /* reset */ true); mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80, 1234); Loading @@ -502,7 +513,7 @@ public class BatteryStatsHistoryTest { NetworkRegistrationInfo.NR_STATE_NONE); BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED); BatteryStats.HistoryItem item = new BatteryStats.HistoryItem(); BatteryStats.HistoryItem item; assertThat(item = iterator.next()).isNotNull(); // First item contains current time only assertThat(item = iterator.next()).isNotNull(); Loading Loading @@ -633,10 +644,17 @@ public class BatteryStatsHistoryTest { @Test public void recordProcStateChange() { mHistory.recordProcessStateChange(200, 200, 42, BatteryConsumer.PROCESS_STATE_BACKGROUND); mHistory.recordProcessStateChange(300, 300, 42, BatteryConsumer.PROCESS_STATE_FOREGROUND); mClock.advance(200); mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, 42, BatteryConsumer.PROCESS_STATE_BACKGROUND); mClock.advance(100); mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, 42, BatteryConsumer.PROCESS_STATE_FOREGROUND); mClock.advance(100); // Large UID, > 0xFFFFFF mHistory.recordProcessStateChange(400, 400, mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, UserHandle.getUid(777, Process.LAST_ISOLATED_UID), BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE); Loading @@ -647,17 +665,17 @@ public class BatteryStatsHistoryTest { assertThat(item = iterator.next()).isNotNull(); String dump = toString(item, /* checkin */ false); assertThat(dump).contains("+200ms"); assertThat(dump).contains("04-03 02:01:00.200"); assertThat(dump).contains("procstate: 42: bg"); assertThat(item = iterator.next()).isNotNull(); dump = toString(item, /* checkin */ false); assertThat(dump).contains("+300ms"); assertThat(dump).contains("04-03 02:01:00.300"); assertThat(dump).contains("procstate: 42: fg"); assertThat(item = iterator.next()).isNotNull(); dump = toString(item, /* checkin */ false); assertThat(dump).contains("+400ms"); assertThat(dump).contains("04-03 02:01:00.400"); assertThat(dump).contains("procstate: u777i999: fgs"); } Loading @@ -672,7 +690,6 @@ public class BatteryStatsHistoryTest { @Test public void getMonotonicHistorySize() { long lastHistorySize = mHistory.getMonotonicHistorySize(); mHistory.forceRecordAllHistory(); mClock.realtime = 1000; mClock.uptime = 1000; Loading
services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java +9 −0 Original line number Diff line number Diff line Loading @@ -40,4 +40,13 @@ public class MockClock extends Clock { public long currentTimeMillis() { return currentTime; } /** * Advances the clock by the given number of milliseconds. */ public void advance(long milliseconds) { realtime += milliseconds; uptime += milliseconds; currentTime += milliseconds; } }