diff options
| author | 2021-03-01 01:05:03 +0000 | |
|---|---|---|
| committer | 2021-03-01 01:05:03 +0000 | |
| commit | cda90b4cf5d47fce56f3b3302667a3a332446f13 (patch) | |
| tree | fb42bf55ca1dc2253802a1727abc1d80e61b797f | |
| parent | c7f1d37c199208337caf3d22d758421e8ed90854 (diff) | |
| parent | 5ab97fffe00dfd0d21a829ae836822c98bab79b3 (diff) | |
Merge "Break out battery history reading into a separate class" into sc-dev
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, |