summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dmitri Plotnikov <dplotnikov@google.com> 2021-03-01 01:05:03 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2021-03-01 01:05:03 +0000
commitcda90b4cf5d47fce56f3b3302667a3a332446f13 (patch)
treefb42bf55ca1dc2253802a1727abc1d80e61b797f
parentc7f1d37c199208337caf3d22d758421e8ed90854 (diff)
parent5ab97fffe00dfd0d21a829ae836822c98bab79b3 (diff)
Merge "Break out battery history reading into a separate class" into sc-dev
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java21
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistoryIterator.java264
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java214
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java115
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java1
5 files changed, 424 insertions, 191 deletions
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index bdb65f33ad1a..879e0a8cfe10 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.BatteryStats;
import android.os.Parcel;
import android.os.StatFs;
@@ -61,6 +63,7 @@ public class BatteryStatsHistory {
public static final String FILE_SUFFIX = ".bin";
private static final int MIN_FREE_SPACE = 100 * 1024 * 1024;
+ @Nullable
private final BatteryStatsImpl mStats;
private final Parcel mHistoryBuffer;
private final File mHistoryDir;
@@ -107,7 +110,8 @@ public class BatteryStatsHistory {
* @param systemDir typically /data/system
* @param historyBuffer The in-memory history buffer.
*/
- public BatteryStatsHistory(BatteryStatsImpl stats, File systemDir, Parcel historyBuffer) {
+ public BatteryStatsHistory(@NonNull BatteryStatsImpl stats, File systemDir,
+ Parcel historyBuffer) {
mStats = stats;
mHistoryBuffer = historyBuffer;
mHistoryDir = new File(systemDir, HISTORY_DIR);
@@ -149,11 +153,10 @@ public class BatteryStatsHistory {
/**
* Used when BatteryStatsImpl object is created from deserialization of a parcel,
* such as Settings app or checkin file.
- * @param stats BatteryStatsImpl object.
- * @param historyBuffer the history buffer inside BatteryStatsImpl
+ * @param historyBuffer the history buffer
*/
- public BatteryStatsHistory(BatteryStatsImpl stats, Parcel historyBuffer) {
- mStats = stats;
+ public BatteryStatsHistory(Parcel historyBuffer) {
+ mStats = null;
mHistoryDir = null;
mHistoryBuffer = historyBuffer;
}
@@ -184,10 +187,16 @@ public class BatteryStatsHistory {
* create next history file.
*/
public void startNextFile() {
+ if (mStats == null) {
+ Slog.wtf(TAG, "mStats should not be null when writing history");
+ return;
+ }
+
if (mFileNumbers.isEmpty()) {
Slog.wtf(TAG, "mFileNumbers should never be empty");
return;
}
+
// The last number in mFileNumbers is the highest number. The next file number is highest
// number plus one.
final int next = mFileNumbers.get(mFileNumbers.size() - 1) + 1;
@@ -357,7 +366,7 @@ public class BatteryStatsHistory {
private boolean skipHead(Parcel p) {
p.setDataPosition(0);
final int version = p.readInt();
- if (version != mStats.VERSION) {
+ if (version != BatteryStatsImpl.VERSION) {
return false;
}
// skip historyBaseTime field.
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
new file mode 100644
index 000000000000..d99af8178feb
--- /dev/null
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.NonNull;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Parcel;
+import android.util.Slog;
+
+import java.util.List;
+
+/**
+ * An iterator for {@link BatteryStats.HistoryItem}'s.
+ */
+public class BatteryStatsHistoryIterator {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "BatteryStatsHistoryItr";
+ private final BatteryStatsHistory mBatteryStatsHistory;
+ private final BatteryStats.HistoryStepDetails mReadHistoryStepDetails =
+ new BatteryStats.HistoryStepDetails();
+ private final String[] mReadHistoryStrings;
+ private final int[] mReadHistoryUids;
+
+ BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history,
+ @NonNull List<BatteryStats.HistoryTag> historyTagPool) {
+ mBatteryStatsHistory = history;
+
+ mBatteryStatsHistory.startIteratingHistory();
+
+ mReadHistoryStrings = new String[historyTagPool.size()];
+ mReadHistoryUids = new int[historyTagPool.size()];
+ for (int i = historyTagPool.size() - 1; i >= 0; i--) {
+ BatteryStats.HistoryTag tag = historyTagPool.get(i);
+ final int idx = tag.poolIdx;
+ mReadHistoryStrings[idx] = tag.string;
+ mReadHistoryUids[idx] = tag.uid;
+ }
+ }
+
+ /**
+ * Retrieves the next HistoryItem from battery history, if available. Returns false if there
+ * are no more items.
+ */
+ public boolean next(BatteryStats.HistoryItem out) {
+ Parcel p = mBatteryStatsHistory.getNextParcel(out);
+ if (p == null) {
+ mBatteryStatsHistory.finishIteratingHistory();
+ return false;
+ }
+
+ final long lastRealtimeMs = out.time;
+ final long lastWalltimeMs = out.currentTime;
+ readHistoryDelta(p, out);
+ if (out.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME
+ && out.cmd != BatteryStats.HistoryItem.CMD_RESET && lastWalltimeMs != 0) {
+ out.currentTime = lastWalltimeMs + (out.time - lastRealtimeMs);
+ }
+ return true;
+ }
+
+ void readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur) {
+ int firstToken = src.readInt();
+ int deltaTimeToken = firstToken & BatteryStatsImpl.DELTA_TIME_MASK;
+ cur.cmd = BatteryStats.HistoryItem.CMD_UPDATE;
+ cur.numReadInts = 1;
+ if (DEBUG) {
+ Slog.i(TAG, "READ DELTA: firstToken=0x" + Integer.toHexString(firstToken)
+ + " deltaTimeToken=" + deltaTimeToken);
+ }
+
+ if (deltaTimeToken < BatteryStatsImpl.DELTA_TIME_ABS) {
+ cur.time += deltaTimeToken;
+ } else if (deltaTimeToken == BatteryStatsImpl.DELTA_TIME_ABS) {
+ cur.readFromParcel(src);
+ if (DEBUG) Slog.i(TAG, "READ DELTA: ABS time=" + cur.time);
+ return;
+ } else if (deltaTimeToken == BatteryStatsImpl.DELTA_TIME_INT) {
+ int delta = src.readInt();
+ cur.time += delta;
+ cur.numReadInts += 1;
+ if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time);
+ } else {
+ long delta = src.readLong();
+ if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time);
+ cur.time += delta;
+ cur.numReadInts += 2;
+ }
+
+ final int batteryLevelInt;
+ if ((firstToken & BatteryStatsImpl.DELTA_BATTERY_LEVEL_FLAG) != 0) {
+ batteryLevelInt = src.readInt();
+ readBatteryLevelInt(batteryLevelInt, cur);
+ cur.numReadInts += 1;
+ if (DEBUG) {
+ Slog.i(TAG, "READ DELTA: batteryToken=0x"
+ + Integer.toHexString(batteryLevelInt)
+ + " batteryLevel=" + cur.batteryLevel
+ + " batteryTemp=" + cur.batteryTemperature
+ + " batteryVolt=" + (int) cur.batteryVoltage);
+ }
+ } else {
+ batteryLevelInt = 0;
+ }
+
+ if ((firstToken & BatteryStatsImpl.DELTA_STATE_FLAG) != 0) {
+ int stateInt = src.readInt();
+ cur.states = (firstToken & BatteryStatsImpl.DELTA_STATE_MASK) | (stateInt
+ & (~BatteryStatsImpl.STATE_BATTERY_MASK));
+ cur.batteryStatus = (byte) ((stateInt >> BatteryStatsImpl.STATE_BATTERY_STATUS_SHIFT)
+ & BatteryStatsImpl.STATE_BATTERY_STATUS_MASK);
+ cur.batteryHealth = (byte) ((stateInt >> BatteryStatsImpl.STATE_BATTERY_HEALTH_SHIFT)
+ & BatteryStatsImpl.STATE_BATTERY_HEALTH_MASK);
+ cur.batteryPlugType = (byte) ((stateInt >> BatteryStatsImpl.STATE_BATTERY_PLUG_SHIFT)
+ & BatteryStatsImpl.STATE_BATTERY_PLUG_MASK);
+ switch (cur.batteryPlugType) {
+ case 1:
+ cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_AC;
+ break;
+ case 2:
+ cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_USB;
+ break;
+ case 3:
+ cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
+ break;
+ }
+ cur.numReadInts += 1;
+ if (DEBUG) {
+ Slog.i(TAG, "READ DELTA: stateToken=0x"
+ + Integer.toHexString(stateInt)
+ + " batteryStatus=" + cur.batteryStatus
+ + " batteryHealth=" + cur.batteryHealth
+ + " batteryPlugType=" + cur.batteryPlugType
+ + " states=0x" + Integer.toHexString(cur.states));
+ }
+ } else {
+ cur.states = (firstToken & BatteryStatsImpl.DELTA_STATE_MASK) | (cur.states
+ & (~BatteryStatsImpl.STATE_BATTERY_MASK));
+ }
+
+ if ((firstToken & BatteryStatsImpl.DELTA_STATE2_FLAG) != 0) {
+ cur.states2 = src.readInt();
+ if (DEBUG) {
+ Slog.i(TAG, "READ DELTA: states2=0x"
+ + Integer.toHexString(cur.states2));
+ }
+ }
+
+ if ((firstToken & BatteryStatsImpl.DELTA_WAKELOCK_FLAG) != 0) {
+ int indexes = src.readInt();
+ int wakeLockIndex = indexes & 0xffff;
+ int wakeReasonIndex = (indexes >> 16) & 0xffff;
+ if (wakeLockIndex != 0xffff) {
+ cur.wakelockTag = cur.localWakelockTag;
+ readHistoryTag(wakeLockIndex, cur.wakelockTag);
+ if (DEBUG) {
+ Slog.i(TAG, "READ DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+ + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+ }
+ } else {
+ cur.wakelockTag = null;
+ }
+ if (wakeReasonIndex != 0xffff) {
+ cur.wakeReasonTag = cur.localWakeReasonTag;
+ readHistoryTag(wakeReasonIndex, cur.wakeReasonTag);
+ if (DEBUG) {
+ Slog.i(TAG, "READ DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+ + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
+ }
+ } else {
+ cur.wakeReasonTag = null;
+ }
+ cur.numReadInts += 1;
+ } else {
+ cur.wakelockTag = null;
+ cur.wakeReasonTag = null;
+ }
+
+ if ((firstToken & BatteryStatsImpl.DELTA_EVENT_FLAG) != 0) {
+ cur.eventTag = cur.localEventTag;
+ final int codeAndIndex = src.readInt();
+ cur.eventCode = (codeAndIndex & 0xffff);
+ final int index = ((codeAndIndex >> 16) & 0xffff);
+ readHistoryTag(index, cur.eventTag);
+ cur.numReadInts += 1;
+ if (DEBUG) {
+ Slog.i(TAG, "READ DELTA: event=" + cur.eventCode + " tag=#"
+ + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
+ + cur.eventTag.string);
+ }
+ } else {
+ cur.eventCode = BatteryStats.HistoryItem.EVENT_NONE;
+ }
+
+ if ((batteryLevelInt & BatteryStatsImpl.BATTERY_DELTA_LEVEL_FLAG) != 0) {
+ cur.stepDetails = mReadHistoryStepDetails;
+ cur.stepDetails.readFromParcel(src);
+ } else {
+ cur.stepDetails = null;
+ }
+
+ if ((firstToken & BatteryStatsImpl.DELTA_BATTERY_CHARGE_FLAG) != 0) {
+ cur.batteryChargeUah = src.readInt();
+ }
+ cur.modemRailChargeMah = src.readDouble();
+ cur.wifiRailChargeMah = src.readDouble();
+ }
+
+ int getHistoryStringPoolSize() {
+ return mReadHistoryStrings.length;
+ }
+
+ int getHistoryStringPoolBytes() {
+ int totalChars = 0;
+ for (int i = mReadHistoryStrings.length - 1; i >= 0; i--) {
+ if (mReadHistoryStrings[i] != null) {
+ totalChars += mReadHistoryStrings[i].length() + 1;
+ }
+ }
+
+ // Each entry is a fixed 12 bytes: 4 for index, 4 for uid, 4 for string size
+ // Each string character is 2 bytes.
+ return (mReadHistoryStrings.length * 12) + (totalChars * 2);
+ }
+
+ String getHistoryTagPoolString(int index) {
+ return mReadHistoryStrings[index];
+ }
+
+ int getHistoryTagPoolUid(int index) {
+ return mReadHistoryUids[index];
+ }
+
+ private void readHistoryTag(int index, BatteryStats.HistoryTag tag) {
+ if (index < mReadHistoryStrings.length) {
+ tag.string = mReadHistoryStrings[index];
+ tag.uid = mReadHistoryUids[index];
+ } else {
+ tag.string = null;
+ tag.uid = 0;
+ }
+ tag.poolIdx = index;
+ }
+
+ private static void readBatteryLevelInt(int batteryLevelInt, BatteryStats.HistoryItem out) {
+ out.batteryLevel = (byte) ((batteryLevelInt & 0xfe000000) >>> 25);
+ out.batteryTemperature = (short) ((batteryLevelInt & 0x01ff8000) >>> 15);
+ out.batteryVoltage = (char) ((batteryLevelInt & 0x00007ffe) >>> 1);
+ }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e4c8ddbbd256..73527d4a80d8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -737,14 +737,11 @@ public class BatteryStatsImpl extends BatteryStats {
protected boolean mRecordingHistory = false;
int mNumHistoryItems;
+ final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
final Parcel mHistoryBuffer = Parcel.obtain();
final HistoryItem mHistoryLastWritten = new HistoryItem();
final HistoryItem mHistoryLastLastWritten = new HistoryItem();
final HistoryItem mHistoryAddTmp = new HistoryItem();
- final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
- String[] mReadHistoryStrings;
- int[] mReadHistoryUids;
- int mReadHistoryChars;
int mNextHistoryTagIdx = 0;
int mNumHistoryTagChars = 0;
int mHistoryBufferLastPos = -1;
@@ -785,6 +782,7 @@ public class BatteryStatsImpl extends BatteryStats {
private final HashMap<Integer, PowerForUid> mUidToPower = ENABLE_FOREGROUND_STATS_COLLECTION
? new HashMap<>() : null;
+ @NonNull
final BatteryStatsHistory mBatteryStatsHistory;
final HistoryItem mHistoryCur = new HistoryItem();
@@ -827,9 +825,9 @@ public class BatteryStatsImpl extends BatteryStats {
long mCurStepStatSoftIrqTimeMs;
long mCurStepStatIdleTimeMs;
+ private BatteryStatsHistoryIterator mBatteryStatsHistoryIterator;
private HistoryItem mHistoryIterator;
private boolean mReadOverflow;
- private boolean mIteratingHistory;
int mStartCount;
@@ -1193,7 +1191,7 @@ public class BatteryStatsImpl extends BatteryStats {
mStatsFile = null;
mCheckinFile = null;
mDailyFile = null;
- mBatteryStatsHistory = null;
+ mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
mHandler = null;
mPlatformIdleStateCallback = null;
mMeasuredEnergyRetriever = null;
@@ -3240,17 +3238,6 @@ public class BatteryStatsImpl extends BatteryStats {
return idx;
}
- private void readHistoryTag(int index, HistoryTag tag) {
- if (index < mReadHistoryStrings.length) {
- tag.string = mReadHistoryStrings[index];
- tag.uid = mReadHistoryUids[index];
- } else {
- tag.string = null;
- tag.uid = 0;
- }
- tag.poolIdx = index;
- }
-
/*
The history delta format uses flags to denote further data in subsequent ints in the parcel.
@@ -3627,137 +3614,6 @@ public class BatteryStatsImpl extends BatteryStats {
mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
}
- public void readHistoryDelta(Parcel src, HistoryItem cur) {
- int firstToken = src.readInt();
- int deltaTimeToken = firstToken&DELTA_TIME_MASK;
- cur.cmd = HistoryItem.CMD_UPDATE;
- cur.numReadInts = 1;
- if (DEBUG) Slog.i(TAG, "READ DELTA: firstToken=0x" + Integer.toHexString(firstToken)
- + " deltaTimeToken=" + deltaTimeToken);
-
- if (deltaTimeToken < DELTA_TIME_ABS) {
- cur.time += deltaTimeToken;
- } else if (deltaTimeToken == DELTA_TIME_ABS) {
- cur.readFromParcel(src);
- if (DEBUG) Slog.i(TAG, "READ DELTA: ABS time=" + cur.time);
- return;
- } else if (deltaTimeToken == DELTA_TIME_INT) {
- int delta = src.readInt();
- cur.time += delta;
- cur.numReadInts += 1;
- if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time);
- } else {
- long delta = src.readLong();
- if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time);
- cur.time += delta;
- cur.numReadInts += 2;
- }
-
- final int batteryLevelInt;
- if ((firstToken&DELTA_BATTERY_LEVEL_FLAG) != 0) {
- batteryLevelInt = src.readInt();
- readBatteryLevelInt(batteryLevelInt, cur);
- cur.numReadInts += 1;
- if (DEBUG) Slog.i(TAG, "READ DELTA: batteryToken=0x"
- + Integer.toHexString(batteryLevelInt)
- + " batteryLevel=" + cur.batteryLevel
- + " batteryTemp=" + cur.batteryTemperature
- + " batteryVolt=" + (int)cur.batteryVoltage);
- } else {
- batteryLevelInt = 0;
- }
-
- if ((firstToken&DELTA_STATE_FLAG) != 0) {
- int stateInt = src.readInt();
- cur.states = (firstToken&DELTA_STATE_MASK) | (stateInt&(~STATE_BATTERY_MASK));
- cur.batteryStatus = (byte)((stateInt>>STATE_BATTERY_STATUS_SHIFT)
- & STATE_BATTERY_STATUS_MASK);
- cur.batteryHealth = (byte)((stateInt>>STATE_BATTERY_HEALTH_SHIFT)
- & STATE_BATTERY_HEALTH_MASK);
- cur.batteryPlugType = (byte)((stateInt>>STATE_BATTERY_PLUG_SHIFT)
- & STATE_BATTERY_PLUG_MASK);
- switch (cur.batteryPlugType) {
- case 1:
- cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_AC;
- break;
- case 2:
- cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_USB;
- break;
- case 3:
- cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
- break;
- }
- cur.numReadInts += 1;
- if (DEBUG) Slog.i(TAG, "READ DELTA: stateToken=0x"
- + Integer.toHexString(stateInt)
- + " batteryStatus=" + cur.batteryStatus
- + " batteryHealth=" + cur.batteryHealth
- + " batteryPlugType=" + cur.batteryPlugType
- + " states=0x" + Integer.toHexString(cur.states));
- } else {
- cur.states = (firstToken&DELTA_STATE_MASK) | (cur.states&(~STATE_BATTERY_MASK));
- }
-
- if ((firstToken&DELTA_STATE2_FLAG) != 0) {
- cur.states2 = src.readInt();
- if (DEBUG) Slog.i(TAG, "READ DELTA: states2=0x"
- + Integer.toHexString(cur.states2));
- }
-
- if ((firstToken&DELTA_WAKELOCK_FLAG) != 0) {
- int indexes = src.readInt();
- int wakeLockIndex = indexes&0xffff;
- int wakeReasonIndex = (indexes>>16)&0xffff;
- if (wakeLockIndex != 0xffff) {
- cur.wakelockTag = cur.localWakelockTag;
- readHistoryTag(wakeLockIndex, cur.wakelockTag);
- if (DEBUG) Slog.i(TAG, "READ DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
- + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
- } else {
- cur.wakelockTag = null;
- }
- if (wakeReasonIndex != 0xffff) {
- cur.wakeReasonTag = cur.localWakeReasonTag;
- readHistoryTag(wakeReasonIndex, cur.wakeReasonTag);
- if (DEBUG) Slog.i(TAG, "READ DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
- + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
- } else {
- cur.wakeReasonTag = null;
- }
- cur.numReadInts += 1;
- } else {
- cur.wakelockTag = null;
- cur.wakeReasonTag = null;
- }
-
- if ((firstToken&DELTA_EVENT_FLAG) != 0) {
- cur.eventTag = cur.localEventTag;
- final int codeAndIndex = src.readInt();
- cur.eventCode = (codeAndIndex&0xffff);
- final int index = ((codeAndIndex>>16)&0xffff);
- readHistoryTag(index, cur.eventTag);
- cur.numReadInts += 1;
- if (DEBUG) Slog.i(TAG, "READ DELTA: event=" + cur.eventCode + " tag=#"
- + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
- + cur.eventTag.string);
- } else {
- cur.eventCode = HistoryItem.EVENT_NONE;
- }
-
- if ((batteryLevelInt&BATTERY_DELTA_LEVEL_FLAG) != 0) {
- cur.stepDetails = mReadHistoryStepDetails;
- cur.stepDetails.readFromParcel(src);
- } else {
- cur.stepDetails = null;
- }
-
- if ((firstToken&DELTA_BATTERY_CHARGE_FLAG) != 0) {
- cur.batteryChargeUah = src.readInt();
- }
- cur.modemRailChargeMah = src.readDouble();
- cur.wifiRailChargeMah = src.readDouble();
- }
-
@Override
public void commitCurrentHistoryBatchLocked() {
mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
@@ -3869,7 +3725,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
private void addHistoryBufferLocked(long elapsedRealtimeMs, byte cmd, HistoryItem cur) {
- if (mIteratingHistory) {
+ if (mBatteryStatsHistoryIterator != null) {
throw new IllegalStateException("Can't do this while iterating history!");
}
mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
@@ -10680,7 +10536,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (systemDir == null) {
mStatsFile = null;
- mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
+ mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
} else {
mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin"));
mBatteryStatsHistory = new BatteryStatsHistory(this, systemDir, mHistoryBuffer);
@@ -10802,7 +10658,7 @@ public class BatteryStatsImpl extends BatteryStats {
mExternalSync = null;
mConstants = new Constants(mHandler);
clearHistoryLocked();
- mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
+ mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
readFromParcel(p);
mPlatformIdleStateCallback = null;
mMeasuredEnergyRetriever = null;
@@ -11180,67 +11036,55 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
@UnsupportedAppUsage
public boolean startIteratingHistoryLocked() {
- mBatteryStatsHistory.startIteratingHistory();
mReadOverflow = false;
- mIteratingHistory = true;
- mReadHistoryStrings = new String[mHistoryTagPool.size()];
- mReadHistoryUids = new int[mHistoryTagPool.size()];
- mReadHistoryChars = 0;
- for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
- final HistoryTag tag = ent.getKey();
- final int idx = ent.getValue();
- mReadHistoryStrings[idx] = tag.string;
- mReadHistoryUids[idx] = tag.uid;
- mReadHistoryChars += tag.string.length() + 1;
- }
+ mBatteryStatsHistoryIterator = createBatteryStatsHistoryIterator();
return true;
}
+ /**
+ * Creates an iterator for battery stats history.
+ */
+ @VisibleForTesting
+ public BatteryStatsHistoryIterator createBatteryStatsHistoryIterator() {
+ ArrayList<HistoryTag> tags = new ArrayList<>(mHistoryTagPool.size());
+ for (Map.Entry<HistoryTag, Integer> entry: mHistoryTagPool.entrySet()) {
+ final HistoryTag tag = entry.getKey();
+ tag.poolIdx = entry.getValue();
+ tags.add(tag);
+ }
+
+ return new BatteryStatsHistoryIterator(mBatteryStatsHistory, tags);
+ }
+
@Override
public int getHistoryStringPoolSize() {
- return mReadHistoryStrings.length;
+ return mBatteryStatsHistoryIterator.getHistoryStringPoolSize();
}
@Override
public int getHistoryStringPoolBytes() {
- // Each entry is a fixed 12 bytes: 4 for index, 4 for uid, 4 for string size
- // Each string character is 2 bytes.
- return (mReadHistoryStrings.length * 12) + (mReadHistoryChars * 2);
+ return mBatteryStatsHistoryIterator.getHistoryStringPoolBytes();
}
@Override
public String getHistoryTagPoolString(int index) {
- return mReadHistoryStrings[index];
+ return mBatteryStatsHistoryIterator.getHistoryTagPoolString(index);
}
@Override
public int getHistoryTagPoolUid(int index) {
- return mReadHistoryUids[index];
+ return mBatteryStatsHistoryIterator.getHistoryTagPoolUid(index);
}
@Override
@UnsupportedAppUsage
public boolean getNextHistoryLocked(HistoryItem out) {
- Parcel p = mBatteryStatsHistory.getNextParcel(out);
- if (p == null) {
- return false;
- }
- final long lastRealtimeMs = out.time;
- final long lastWalltimeMs = out.currentTime;
- readHistoryDelta(p, out);
- if (out.cmd != HistoryItem.CMD_CURRENT_TIME
- && out.cmd != HistoryItem.CMD_RESET && lastWalltimeMs != 0) {
- out.currentTime = lastWalltimeMs + (out.time - lastRealtimeMs);
- }
- return true;
+ return mBatteryStatsHistoryIterator.next(out);
}
@Override
public void finishIteratingHistoryLocked() {
- mBatteryStatsHistory.finishIteratingHistory();
- mIteratingHistory = false;
- mReadHistoryStrings = null;
- mReadHistoryUids = null;
+ mBatteryStatsHistoryIterator = null;
}
@Override
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java
new file mode 100644
index 000000000000..263daf0cd625
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Process;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BatteryStatsHistoryIteratorTest {
+ private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+
+ @Rule
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+
+ @Test
+ public void testIterator() {
+ MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ batteryStats.setRecordAllHistoryLocked(true);
+ batteryStats.forceRecordAllHistory();
+
+ mStatsRule.setTime(1000, 1000);
+ 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.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0, 2_000_000,
+ 2_000_000, 2_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);
+
+ final BatteryStatsHistoryIterator iterator =
+ batteryStats.createBatteryStatsHistoryIterator();
+
+ 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, 2_400_000, 80, 2_000_000);
+
+ assertThat(iterator.next(item)).isTrue();
+ assertHistoryItem(item,
+ BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE,
+ null, 0, 2_400_000, 80, 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, 2_400_000, 80, 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, 2_400_000, 80, 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);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index d276bc34d05a..74c37ada2054 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -30,6 +30,7 @@ import org.junit.runners.Suite;
BatteryStatsDualTimerTest.class,
BatteryStatsDurationTimerTest.class,
BatteryStatsHelperTest.class,
+ BatteryStatsHistoryIteratorTest.class,
BatteryStatsHistoryTest.class,
BatteryStatsImplTest.class,
BatteryStatsNoteTest.class,