summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dmitri Plotnikov <dplotnikov@google.com> 2025-01-02 16:19:49 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-01-02 16:19:49 -0800
commit43e4dede09bb1f995323be25f72f8d0a666a526f (patch)
treeb63dd25d80e02718871615438a62ff68880c60d1
parentbfc1502ecf6af1afcf4e2f52a515803ff5239ea6 (diff)
parent398548cac7d6e1cc27d3ffc69be998cfd7add79c (diff)
Merge changes I612f840f,I2c4a3eab into main
* changes: Change battery history timestamp format Add extended_battery_history_continuous_collection_enabled flag
-rw-r--r--core/java/android/os/BatteryStats.java102
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistoryIterator.java19
-rw-r--r--services/core/java/com/android/server/power/stats/flags.aconfig10
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java73
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java9
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;
+ }
}