diff options
| author | 2025-01-02 16:19:49 -0800 | |
|---|---|---|
| committer | 2025-01-02 16:19:49 -0800 | |
| commit | 43e4dede09bb1f995323be25f72f8d0a666a526f (patch) | |
| tree | b63dd25d80e02718871615438a62ff68880c60d1 | |
| parent | bfc1502ecf6af1afcf4e2f52a515803ff5239ea6 (diff) | |
| parent | 398548cac7d6e1cc27d3ffc69be998cfd7add79c (diff) | |
Merge changes I612f840f,I2c4a3eab into main
* changes:
Change battery history timestamp format
Add extended_battery_history_continuous_collection_enabled flag
5 files changed, 151 insertions, 62 deletions
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index c41e626444c9..298cec1674b2 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -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 @@ -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; @@ -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; @@ -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; @@ -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; @@ -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(" "); - TimeUtils.formatDuration( - rec.time - baseTime, item, TimeUtils.HUNDRED_DAY_FIELD_LEN); - item.append(" ("); - item.append(rec.numReadInts); - 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(','); @@ -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) { @@ -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) { - dumpHistoryTagPoolLocked(pw, checkin); + 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; @@ -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("):"); } } diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java index e2005d7cdedf..ee897cd37cd3 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java @@ -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) { @@ -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; } diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig index 5e048810cc97..c8dbbd29823c 100644 --- a/services/core/java/com/android/server/power/stats/flags.aconfig +++ b/services/core/java/com/android/server/power/stats/flags.aconfig @@ -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 + } +} diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java index bc81feb3f7c7..164eec6fbc49 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java @@ -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; @@ -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; @@ -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; @@ -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 @@ -145,8 +159,6 @@ public class BatteryStatsHistoryTest { @Test public void testAtraceExcludedState() { - mHistory.forceRecordAllHistory(); - Mockito.when(mTracer.tracingEnabled()).thenReturn(true); mHistory.recordStateStartEvent(mClock.elapsedRealtime(), @@ -354,8 +366,6 @@ public class BatteryStatsHistoryTest { } private void prepareMultiFileHistory() { - mHistory.forceRecordAllHistory(); - mClock.realtime = 1000; mClock.uptime = 1000; mHistory.recordEvent(mClock.realtime, mClock.uptime, @@ -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; @@ -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]"); @@ -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); @@ -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(); @@ -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); @@ -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"); } @@ -672,7 +690,6 @@ public class BatteryStatsHistoryTest { @Test public void getMonotonicHistorySize() { long lastHistorySize = mHistory.getMonotonicHistorySize(); - mHistory.forceRecordAllHistory(); mClock.realtime = 1000; mClock.uptime = 1000; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java index 5e57cc36797b..215ac409007e 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java @@ -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; + } } |