From 20720fa188a25c8bc5b1f351c61fa724a5744d5d Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Wed, 24 Feb 2021 13:51:31 -0800 Subject: [PATCH] Add battery history to BatteryStatsUsage Bug: 173745486 Test: atest FrameworksCoreTests:com.android.internal.os.BatteryUsageStatsProviderTest Change-Id: Icfd9e49b68f1be99312c88ac889b06aa669367ef --- core/java/android/os/BatteryUsageStats.java | 73 ++++++++++++++++ .../android/os/BatteryUsageStatsQuery.java | 13 ++- .../os/BatteryStatsHistoryIterator.java | 2 +- .../os/BatteryUsageStatsProvider.java | 15 ++++ .../os/BatteryUsageStatsProviderTest.java | 85 +++++++++++++++++++ 5 files changed, 184 insertions(+), 4 deletions(-) diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 35a5a7c7c1d05..de7b88575e98c 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -20,6 +20,9 @@ import android.annotation.NonNull; import android.util.Range; import android.util.SparseArray; +import com.android.internal.os.BatteryStatsHistory; +import com.android.internal.os.BatteryStatsHistoryIterator; + import java.util.ArrayList; import java.util.List; @@ -37,12 +40,16 @@ public final class BatteryUsageStats implements Parcelable { private final ArrayList mUidBatteryConsumers; private final ArrayList mSystemBatteryConsumers; private final ArrayList mUserBatteryConsumers; + private final Parcel mHistoryBuffer; + private final List mHistoryTagPool; private BatteryUsageStats(@NonNull Builder builder) { mStatsStartRealtimeMs = builder.mStatsStartRealtimeMs; mDischargePercentage = builder.mDischargePercentage; mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah; mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah; + mHistoryBuffer = builder.mHistoryBuffer; + mHistoryTagPool = builder.mHistoryTagPool; double totalPower = 0; @@ -125,6 +132,19 @@ public final class BatteryUsageStats implements Parcelable { return mUserBatteryConsumers; } + /** + * Returns an iterator for {@link android.os.BatteryStats.HistoryItem}'s. + */ + @NonNull + public BatteryStatsHistoryIterator iterateBatteryStatsHistory() { + if (mHistoryBuffer == null) { + throw new IllegalStateException( + "Battery history was not requested in the BatteryUsageStatsQuery"); + } + return new BatteryStatsHistoryIterator(new BatteryStatsHistory(mHistoryBuffer), + mHistoryTagPool); + } + @Override public int describeContents() { return 0; @@ -142,6 +162,29 @@ public final class BatteryUsageStats implements Parcelable { source.readParcelableList(mSystemBatteryConsumers, getClass().getClassLoader()); mUserBatteryConsumers = new ArrayList<>(); source.readParcelableList(mUserBatteryConsumers, getClass().getClassLoader()); + if (source.readBoolean()) { + mHistoryBuffer = Parcel.obtain(); + mHistoryBuffer.setDataSize(0); + mHistoryBuffer.setDataPosition(0); + + int historyBufferSize = source.readInt(); + int curPos = source.dataPosition(); + mHistoryBuffer.appendFrom(source, curPos, historyBufferSize); + source.setDataPosition(curPos + historyBufferSize); + + int historyTagCount = source.readInt(); + mHistoryTagPool = new ArrayList<>(historyTagCount); + for (int i = 0; i < historyTagCount; i++) { + BatteryStats.HistoryTag tag = new BatteryStats.HistoryTag(); + tag.string = source.readString(); + tag.uid = source.readInt(); + tag.poolIdx = source.readInt(); + mHistoryTagPool.add(tag); + } + } else { + mHistoryBuffer = null; + mHistoryTagPool = null; + } } @Override @@ -154,6 +197,23 @@ public final class BatteryUsageStats implements Parcelable { dest.writeParcelableList(mUidBatteryConsumers, flags); dest.writeParcelableList(mSystemBatteryConsumers, flags); dest.writeParcelableList(mUserBatteryConsumers, flags); + if (mHistoryBuffer != null) { + dest.writeBoolean(true); + + final int historyBufferSize = mHistoryBuffer.dataSize(); + dest.writeInt(historyBufferSize); + dest.appendFrom(mHistoryBuffer, 0, historyBufferSize); + + dest.writeInt(mHistoryTagPool.size()); + for (int i = mHistoryTagPool.size() - 1; i >= 0; i--) { + final BatteryStats.HistoryTag tag = mHistoryTagPool.get(i); + dest.writeString(tag.string); + dest.writeInt(tag.uid); + dest.writeInt(tag.poolIdx); + } + } else { + dest.writeBoolean(false); + } } @NonNull @@ -183,6 +243,8 @@ public final class BatteryUsageStats implements Parcelable { new SparseArray<>(); private final SparseArray mUserBatteryConsumerBuilders = new SparseArray<>(); + private Parcel mHistoryBuffer; + private List mHistoryTagPool; public Builder(int customPowerComponentCount, int customTimeComponentCount) { mCustomPowerComponentCount = customPowerComponentCount; @@ -226,6 +288,17 @@ public final class BatteryUsageStats implements Parcelable { return this; } + /** + * Sets the parceled recent history. + */ + @NonNull + public Builder setBatteryHistory(Parcel historyBuffer, + List historyTagPool) { + mHistoryBuffer = historyBuffer; + mHistoryTagPool = historyTagPool; + return this; + } + /** * Creates or returns a exiting UidBatteryConsumer, which represents battery attribution * data for an individual UID. diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java index 17cb735eea492..9518bf197fa08 100644 --- a/core/java/android/os/BatteryUsageStatsQuery.java +++ b/core/java/android/os/BatteryUsageStatsQuery.java @@ -40,6 +40,7 @@ public final class BatteryUsageStatsQuery implements Parcelable { */ @IntDef(flag = true, prefix = { "FLAG_BATTERY_USAGE_STATS_" }, value = { FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL, + FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY, }) @Retention(RetentionPolicy.SOURCE) public @interface BatteryUsageStatsFlags {} @@ -53,6 +54,12 @@ public final class BatteryUsageStatsQuery implements Parcelable { */ public static final int FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL = 1; + /** + * Indicates that battery history should be included in the BatteryUsageStats. + * @hide + */ + public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY = 2; + private final int mFlags; @NonNull private final int[] mUserIds; @@ -146,10 +153,10 @@ public final class BatteryUsageStatsQuery implements Parcelable { } /** - * Sets flags to modify the behavior of {@link BatteryStatsManager#getBatteryUsageStats}. + * Requests that battery history be included in the BatteryUsageStats. */ - public Builder setFlags(@BatteryUsageStatsFlags int flags) { - mFlags = flags; + public Builder includeBatteryHistory() { + mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY; return this; } diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java index d99af8178feb3..1871ac5074c90 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java @@ -36,7 +36,7 @@ public class BatteryStatsHistoryIterator { private final String[] mReadHistoryStrings; private final int[] mReadHistoryUids; - BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, + public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, @NonNull List historyTagPool) { mBatteryStatsHistory = history; diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index 0163acc295fbe..15b584d1fd06a 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -30,6 +30,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Uses accumulated battery stats data and PowerCalculators to produce power @@ -152,6 +153,20 @@ public class BatteryUsageStatsProvider { query); } + if ((query.getFlags() + & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) { + ArrayList tags = new ArrayList<>( + mStats.mHistoryTagPool.size()); + for (Map.Entry entry : + mStats.mHistoryTagPool.entrySet()) { + final BatteryStats.HistoryTag tag = entry.getKey(); + tag.poolIdx = entry.getValue(); + tags.add(tag); + } + + batteryUsageStatsBuilder.setBatteryHistory(mStats.mHistoryBuffer, tags); + } + return batteryUsageStatsBuilder.build(); } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java index c4b7796b49cf6..0f591433a84e7 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java @@ -20,8 +20,11 @@ import static com.google.common.truth.Truth.assertThat; import android.app.ActivityManager; import android.content.Context; +import android.os.BatteryManager; +import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; +import android.os.Parcel; import android.os.Process; import android.os.UidBatteryConsumer; @@ -73,4 +76,86 @@ public class BatteryUsageStatsProviderTest { assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND)) .isEqualTo(10 * MINUTE_IN_MS); } + + @Test + public void testWriteAndReadHistory() { + MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + batteryStats.setRecordAllHistoryLocked(true); + batteryStats.forceRecordAllHistory(); + + batteryStats.setNoAutoReset(true); + + batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100, + /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000, + 1_000_000, 1_000_000); + + batteryStats.noteAlarmStartLocked("foo", null, APP_UID, 3_000_000, 2_000_000); + batteryStats.noteAlarmFinishLocked("foo", null, APP_UID, 3_001_000, 2_001_000); + + Context context = InstrumentationRegistry.getContext(); + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats); + + final BatteryUsageStats batteryUsageStats = + provider.getBatteryUsageStats( + new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build()); + + Parcel in = Parcel.obtain(); + batteryUsageStats.writeToParcel(in, 0); + final byte[] bytes = in.marshall(); + + Parcel out = Parcel.obtain(); + out.unmarshall(bytes, 0, bytes.length); + out.setDataPosition(0); + + BatteryUsageStats unparceled = BatteryUsageStats.CREATOR.createFromParcel(out); + + final BatteryStatsHistoryIterator iterator = + unparceled.iterateBatteryStatsHistory(); + BatteryStats.HistoryItem item = new BatteryStats.HistoryItem(); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_RESET, BatteryStats.HistoryItem.EVENT_NONE, + null, 0, 3_600_000, 90, 1_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE, + null, 0, 3_600_000, 90, 1_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE, + null, 0, 3_600_000, 90, 2_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, + BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_START, + "foo", APP_UID, 3_600_000, 90, 3_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, + BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_FINISH, + "foo", APP_UID, 3_600_000, 90, 3_001_000); + + assertThat(iterator.next(item)).isFalse(); + } + + private void assertHistoryItem(BatteryStats.HistoryItem item, int command, int eventCode, + String tag, int uid, int batteryChargeUah, int batteryLevel, long elapsedTimeMs) { + assertThat(item.cmd).isEqualTo(command); + assertThat(item.eventCode).isEqualTo(eventCode); + if (tag == null) { + assertThat(item.eventTag).isNull(); + } else { + assertThat(item.eventTag.string).isEqualTo(tag); + assertThat(item.eventTag.uid).isEqualTo(uid); + } + assertThat(item.batteryChargeUah).isEqualTo(batteryChargeUah); + assertThat(item.batteryLevel).isEqualTo(batteryLevel); + + assertThat(item.time).isEqualTo(elapsedTimeMs); + } }