Loading core/java/android/os/BatteryStats.java +1 −1 Original line number Diff line number Diff line Loading @@ -1854,7 +1854,7 @@ public abstract class BatteryStats { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public HistoryItem next; // The time of this event in milliseconds, as per SystemClock.elapsedRealtime(). // The time of this event in milliseconds, as per MonotonicClock.monotonicTime(). @UnsupportedAppUsage public long time; Loading core/java/com/android/internal/os/BatteryStatsHistory.java +61 −62 Original line number Diff line number Diff line Loading @@ -37,7 +37,6 @@ import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -78,7 +77,7 @@ public class BatteryStatsHistory { private static final String TAG = "BatteryStatsHistory"; // Current on-disk Parcel version. Must be updated when the format of the parcelable changes private static final int VERSION = 209; private static final int VERSION = 210; private static final String HISTORY_DIR = "battery-history"; private static final String FILE_SUFFIX = ".bh"; Loading Loading @@ -194,10 +193,11 @@ public class BatteryStatsHistory { private int mNextHistoryTagIdx = 0; private int mNumHistoryTagChars = 0; private int mHistoryBufferLastPos = -1; private long mLastHistoryElapsedRealtimeMs = 0; private long mTrackRunningHistoryElapsedRealtimeMs = 0; private long mTrackRunningHistoryUptimeMs = 0; private long mHistoryBaseTimeMs; private final MonotonicClock mMonotonicClock; // Monotonic time when we started writing to the history buffer private long mHistoryBufferStartTime; private final ArraySet<PowerStats.Descriptor> mWrittenPowerStatsDescriptors = new ArraySet<>(); private byte mLastHistoryStepLevel = 0; private boolean mMutable = true; Loading Loading @@ -307,23 +307,26 @@ public class BatteryStatsHistory { * @param maxHistoryBufferSize the most amount of RAM to used for buffering of history steps */ public BatteryStatsHistory(File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) { HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, MonotonicClock monotonicClock) { this(Parcel.obtain(), systemDir, maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator, clock, new TraceDelegate()); stepDetailsCalculator, clock, monotonicClock, new TraceDelegate()); initHistoryBuffer(); } @VisibleForTesting public BatteryStatsHistory(Parcel historyBuffer, File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer) { HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, MonotonicClock monotonicClock, TraceDelegate tracer) { this(historyBuffer, systemDir, maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator, clock, tracer, null); clock, monotonicClock, tracer, null); } private BatteryStatsHistory(Parcel historyBuffer, File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, MonotonicClock monotonicClock, TraceDelegate tracer, BatteryStatsHistory writableHistory) { mHistoryBuffer = historyBuffer; mSystemDir = systemDir; Loading @@ -332,6 +335,7 @@ public class BatteryStatsHistory { mStepDetailsCalculator = stepDetailsCalculator; mTracer = tracer; mClock = clock; mMonotonicClock = monotonicClock; mWritableHistory = writableHistory; if (mWritableHistory != null) { mMutable = false; Loading Loading @@ -381,16 +385,18 @@ public class BatteryStatsHistory { } private BatteryHistoryFile makeBatteryHistoryFile() { return new BatteryHistoryFile(mHistoryDir, mClock.elapsedRealtime() + mHistoryBaseTimeMs); return new BatteryHistoryFile(mHistoryDir, mMonotonicClock.monotonicTime()); } public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) { HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, MonotonicClock monotonicClock) { mMaxHistoryFiles = maxHistoryFiles; mMaxHistoryBufferSize = maxHistoryBufferSize; mStepDetailsCalculator = stepDetailsCalculator; mTracer = new TraceDelegate(); mClock = clock; mMonotonicClock = monotonicClock; mHistoryBuffer = Parcel.obtain(); mSystemDir = null; Loading @@ -417,16 +423,16 @@ public class BatteryStatsHistory { mHistoryBuffer = Parcel.obtain(); mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length); mMonotonicClock = null; readFromParcel(parcel, true /* useBlobs */); } private void initHistoryBuffer() { mHistoryBaseTimeMs = 0; mLastHistoryElapsedRealtimeMs = 0; mTrackRunningHistoryElapsedRealtimeMs = 0; mTrackRunningHistoryUptimeMs = 0; mWrittenPowerStatsDescriptors.clear(); mHistoryBufferStartTime = mMonotonicClock.monotonicTime(); mHistoryBuffer.setDataSize(0); mHistoryBuffer.setDataPosition(0); mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2); Loading Loading @@ -466,7 +472,7 @@ public class BatteryStatsHistory { historyBufferCopy.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize()); return new BatteryStatsHistory(historyBufferCopy, mSystemDir, 0, 0, null, null, null, this); null, this); } } Loading @@ -491,7 +497,7 @@ public class BatteryStatsHistory { * When {@link #mHistoryBuffer} reaches {@link BatteryStatsImpl.Constants#MAX_HISTORY_BUFFER}, * create next history file. */ public void startNextFile() { public void startNextFile(long elapsedRealtimeMs) { if (mMaxHistoryFiles == 0) { Slog.wtf(TAG, "mMaxHistoryFiles should not be zero when writing history"); return; Loading @@ -517,6 +523,7 @@ public class BatteryStatsHistory { Slog.e(TAG, "Could not create history file: " + mActiveFile.getBaseFile()); } mHistoryBufferStartTime = mMonotonicClock.monotonicTime(elapsedRealtimeMs); mHistoryBuffer.setDataSize(0); mHistoryBuffer.setDataPosition(0); mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2); Loading Loading @@ -699,9 +706,12 @@ public class BatteryStatsHistory { if (mHistoryParcels != null) { while (mParcelIndex < mHistoryParcels.size()) { final Parcel p = mHistoryParcels.get(mParcelIndex++); if (!skipHead(p)) { if (!verifyVersion(p)) { continue; } // skip monotonic time field. p.readLong(); final int bufSize = p.readInt(); final int curPos = p.dataPosition(); mCurrentParcelEnd = curPos + bufSize; Loading Loading @@ -745,24 +755,36 @@ public class BatteryStatsHistory { } out.unmarshall(raw, 0, raw.length); out.setDataPosition(0); return skipHead(out); if (!verifyVersion(out)) { return false; } // skip monotonic time field. out.readLong(); return true; } /** * Skip the header part of history parcel. * Verify header part of history parcel. * * @param p history parcel to skip head. * @return true if version match, false if not. */ private boolean skipHead(Parcel p) { private boolean verifyVersion(Parcel p) { p.setDataPosition(0); final int version = p.readInt(); if (version != VERSION) { return false; return version == VERSION; } // skip historyBaseTime field. p.readLong(); return true; /** * Extracts the monotonic time, as per {@link MonotonicClock}, from the supplied battery history * buffer. */ public long getHistoryBufferStartTime(Parcel p) { int pos = p.dataPosition(); p.setDataPosition(0); p.readInt(); // Skip the version field long monotonicTime = p.readLong(); p.setDataPosition(pos); return monotonicTime; } /** Loading Loading @@ -1438,7 +1460,8 @@ public class BatteryStatsHistory { throw new ConcurrentModificationException("Battery history is not writable"); } final long timeDiffMs = (mHistoryBaseTimeMs + elapsedRealtimeMs) - mHistoryLastWritten.time; final long timeDiffMs = mMonotonicClock.monotonicTime(elapsedRealtimeMs) - mHistoryLastWritten.time; final int diffStates = mHistoryLastWritten.states ^ cur.states; final int diffStates2 = mHistoryLastWritten.states2 ^ cur.states2; final int lastDiffStates = mHistoryLastWritten.states ^ mHistoryLastLastWritten.states; Loading Loading @@ -1476,7 +1499,9 @@ public class BatteryStatsHistory { mHistoryBuffer.setDataSize(mHistoryBufferLastPos); mHistoryBuffer.setDataPosition(mHistoryBufferLastPos); mHistoryBufferLastPos = -1; elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTimeMs; elapsedRealtimeMs -= timeDiffMs; // If the last written history had a wakelock tag, we need to retain it. // Note that the condition above made sure that we aren't in a case where // both it and the current history item have a wakelock tag. Loading Loading @@ -1513,7 +1538,7 @@ public class BatteryStatsHistory { HistoryItem copy = new HistoryItem(); copy.setTo(cur); startNextFile(); startNextFile(elapsedRealtimeMs); // startRecordingHistory will reset mHistoryCur. startRecordingHistory(elapsedRealtimeMs, uptimeMs, false); Loading Loading @@ -1548,7 +1573,7 @@ public class BatteryStatsHistory { mHistoryBufferLastPos = mHistoryBuffer.dataPosition(); mHistoryLastLastWritten.setTo(mHistoryLastWritten); final boolean hasTags = mHistoryLastWritten.tagsFirstOccurrence || cur.tagsFirstOccurrence; mHistoryLastWritten.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur); mHistoryLastWritten.setTo(mMonotonicClock.monotonicTime(elapsedRealtimeMs), cmd, cur); if (mHistoryLastWritten.time < mHistoryLastLastWritten.time - 60000) { Slog.wtf(TAG, "Significantly earlier event written to battery history:" + " time=" + mHistoryLastWritten.time Loading @@ -1556,7 +1581,6 @@ public class BatteryStatsHistory { } mHistoryLastWritten.tagsFirstOccurrence = hasTags; writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten); mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs; cur.wakelockTag = null; cur.wakeReasonTag = null; cur.eventCode = HistoryItem.EVENT_NONE; Loading Loading @@ -1926,6 +1950,10 @@ public class BatteryStatsHistory { return; } // Save the monotonic time first, so that even if the history write below fails, // we still wouldn't end up with overlapping history timelines. mMonotonicClock.write(); Parcel p = Parcel.obtain(); try { final long start = SystemClock.uptimeMillis(); Loading @@ -1951,8 +1979,7 @@ public class BatteryStatsHistory { return; } final long historyBaseTime = in.readLong(); mHistoryBufferStartTime = in.readLong(); mHistoryBuffer.setDataSize(0); mHistoryBuffer.setDataPosition(0); Loading @@ -1972,39 +1999,11 @@ public class BatteryStatsHistory { mHistoryBuffer.appendFrom(in, curPos, bufSize); in.setDataPosition(curPos + bufSize); } mHistoryBaseTimeMs = historyBaseTime; if (DEBUG) { StringBuilder sb = new StringBuilder(128); sb.append("****************** NEW mHistoryBaseTimeMs: "); TimeUtils.formatDuration(mHistoryBaseTimeMs, sb); Slog.i(TAG, sb.toString()); } if (mHistoryBaseTimeMs > 0) { long elapsedRealtimeMs = mClock.elapsedRealtime(); mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs; mHistoryBaseTimeMs = mHistoryBaseTimeMs - elapsedRealtimeMs + 1; if (DEBUG) { StringBuilder sb = new StringBuilder(128); sb.append("****************** ADJUSTED mHistoryBaseTimeMs: "); TimeUtils.formatDuration(mHistoryBaseTimeMs, sb); Slog.i(TAG, sb.toString()); } } } private void writeHistoryBuffer(Parcel out) { if (DEBUG) { StringBuilder sb = new StringBuilder(128); sb.append("****************** WRITING mHistoryBaseTimeMs: "); TimeUtils.formatDuration(mHistoryBaseTimeMs, sb); sb.append(" mLastHistoryElapsedRealtimeMs: "); TimeUtils.formatDuration(mLastHistoryElapsedRealtimeMs, sb); Slog.i(TAG, sb.toString()); } out.writeInt(BatteryStatsHistory.VERSION); out.writeLong(mHistoryBaseTimeMs + mLastHistoryElapsedRealtimeMs); out.writeLong(mHistoryBufferStartTime); out.writeInt(mHistoryBuffer.dataSize()); if (DEBUG) { Slog.i(TAG, "***************** WRITING HISTORY: " Loading core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +15 −10 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.internal.os; import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import android.os.BatteryManager; import android.os.BatteryStats; Loading @@ -34,8 +33,8 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor private static final boolean DEBUG = false; private static final String TAG = "BatteryStatsHistoryItr"; private final BatteryStatsHistory mBatteryStatsHistory; private final @CurrentTimeMillisLong long mStartTimeMs; private final @CurrentTimeMillisLong long mEndTimeMs; private final long mStartTimeMs; private final long mEndTimeMs; private final BatteryStats.HistoryStepDetails mReadHistoryStepDetails = new BatteryStats.HistoryStepDetails(); private final SparseArray<BatteryStats.HistoryTag> mHistoryTags = new SparseArray<>(); Loading @@ -43,10 +42,10 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor new PowerStats.DescriptorRegistry(); private BatteryStats.HistoryItem mHistoryItem = new BatteryStats.HistoryItem(); private boolean mNextItemReady; private boolean mTimeInitialized; public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, @CurrentTimeMillisLong long startTimeMs, @CurrentTimeMillisLong long endTimeMs) { public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs, long endTimeMs) { mBatteryStatsHistory = history; mStartTimeMs = startTimeMs; mEndTimeMs = (endTimeMs != 0) ? endTimeMs : Long.MAX_VALUE; Loading Loading @@ -82,7 +81,12 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor break; } final long lastRealtimeMs = mHistoryItem.time; if (!mTimeInitialized) { mHistoryItem.time = mBatteryStatsHistory.getHistoryBufferStartTime(p); mTimeInitialized = true; } final long lastMonotonicTimeMs = mHistoryItem.time; final long lastWalltimeMs = mHistoryItem.currentTime; try { readHistoryDelta(p, mHistoryItem); Loading @@ -93,12 +97,13 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor if (mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME && mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_RESET && lastWalltimeMs != 0) { mHistoryItem.currentTime = lastWalltimeMs + (mHistoryItem.time - lastRealtimeMs); mHistoryItem.currentTime = lastWalltimeMs + (mHistoryItem.time - lastMonotonicTimeMs); } if (mEndTimeMs != 0 && mHistoryItem.currentTime >= mEndTimeMs) { if (mEndTimeMs != 0 && mHistoryItem.time >= mEndTimeMs) { break; } if (mHistoryItem.currentTime >= mStartTimeMs) { if (mHistoryItem.time >= mStartTimeMs) { mNextItemReady = true; return; } Loading core/java/com/android/internal/os/MonotonicClock.java 0 → 100644 +145 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.util.AtomicFile; import android.util.Log; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; /** * A clock that is similar to SystemClock#elapsedRealtime(), except that it is not reset * on reboot, but keeps going. */ public class MonotonicClock { private static final String TAG = "MonotonicClock"; private static final String XML_TAG_MONOTONIC_TIME = "monotonic_time"; private static final String XML_ATTR_TIMESHIFT = "timeshift"; private final AtomicFile mFile; private final Clock mClock; private long mTimeshift; public MonotonicClock(File file) { mFile = new AtomicFile(file); mClock = Clock.SYSTEM_CLOCK; read(); } public MonotonicClock(long monotonicTime, @NonNull Clock clock) { mClock = clock; mFile = null; mTimeshift = monotonicTime - mClock.elapsedRealtime(); } /** * Returns time in milliseconds, based on SystemClock.elapsedTime, adjusted so that * after a device reboot the time keeps increasing. */ public long monotonicTime() { return monotonicTime(mClock.elapsedRealtime()); } /** * Like {@link #monotonicTime()}, except the elapsed time is supplied as an argument instead * of being read from the Clock. */ public long monotonicTime(long elapsedRealtimeMs) { return mTimeshift + elapsedRealtimeMs; } private void read() { if (!mFile.exists()) { return; } try { readXml(new ByteArrayInputStream(mFile.readFully()), Xml.newBinaryPullParser()); } catch (IOException e) { Log.e(TAG, "Cannot load monotonic clock from " + mFile.getBaseFile(), e); } } /** * Saves the timeshift into a file. Call this method just before system shutdown, after * writing the last battery history event. */ public void write() { if (mFile == null) { return; } mFile.write(out -> { try { writeXml(out, Xml.newBinarySerializer()); out.close(); } catch (IOException e) { Log.e(TAG, "Cannot write monotonic clock to " + mFile.getBaseFile(), e); } }); } /** * Parses an XML file containing the persistent state of the monotonic clock. */ @VisibleForTesting public void readXml(InputStream inputStream, TypedXmlPullParser parser) throws IOException { long savedTimeshift = 0; try { parser.setInput(inputStream, StandardCharsets.UTF_8.name()); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG && parser.getName().equals(XML_TAG_MONOTONIC_TIME)) { savedTimeshift = parser.getAttributeLong(null, XML_ATTR_TIMESHIFT); } eventType = parser.next(); } } catch (XmlPullParserException e) { throw new IOException(e); } mTimeshift = savedTimeshift - mClock.elapsedRealtime(); } /** * Creates an XML file containing the persistent state of the monotonic clock. */ @VisibleForTesting public void writeXml(OutputStream out, TypedXmlSerializer serializer) throws IOException { serializer.setOutput(out, StandardCharsets.UTF_8.name()); serializer.startDocument(null, true); serializer.startTag(null, XML_TAG_MONOTONIC_TIME); serializer.attributeLong(null, XML_ATTR_TIMESHIFT, monotonicTime()); serializer.endTag(null, XML_TAG_MONOTONIC_TIME); serializer.endDocument(); } } core/java/com/android/internal/os/OWNERS +2 −6 Original line number Diff line number Diff line Loading @@ -5,14 +5,10 @@ per-file *Binder* = file:BINDER_OWNERS per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS # BatteryStats per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS per-file BatteryStats* = file:/BATTERY_STATS_OWNERS per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS per-file *ChargeCalculator* = file:/BATTERY_STATS_OWNERS per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS per-file *PowerStats* = file:/BATTERY_STATS_OWNERS per-file *Kernel* = file:/BATTERY_STATS_OWNERS per-file *Clock* = file:/BATTERY_STATS_OWNERS per-file *MultiState* = file:/BATTERY_STATS_OWNERS per-file *PowerProfile* = file:/BATTERY_STATS_OWNERS per-file *PowerStats* = file:/BATTERY_STATS_OWNERS Loading
core/java/android/os/BatteryStats.java +1 −1 Original line number Diff line number Diff line Loading @@ -1854,7 +1854,7 @@ public abstract class BatteryStats { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public HistoryItem next; // The time of this event in milliseconds, as per SystemClock.elapsedRealtime(). // The time of this event in milliseconds, as per MonotonicClock.monotonicTime(). @UnsupportedAppUsage public long time; Loading
core/java/com/android/internal/os/BatteryStatsHistory.java +61 −62 Original line number Diff line number Diff line Loading @@ -37,7 +37,6 @@ import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -78,7 +77,7 @@ public class BatteryStatsHistory { private static final String TAG = "BatteryStatsHistory"; // Current on-disk Parcel version. Must be updated when the format of the parcelable changes private static final int VERSION = 209; private static final int VERSION = 210; private static final String HISTORY_DIR = "battery-history"; private static final String FILE_SUFFIX = ".bh"; Loading Loading @@ -194,10 +193,11 @@ public class BatteryStatsHistory { private int mNextHistoryTagIdx = 0; private int mNumHistoryTagChars = 0; private int mHistoryBufferLastPos = -1; private long mLastHistoryElapsedRealtimeMs = 0; private long mTrackRunningHistoryElapsedRealtimeMs = 0; private long mTrackRunningHistoryUptimeMs = 0; private long mHistoryBaseTimeMs; private final MonotonicClock mMonotonicClock; // Monotonic time when we started writing to the history buffer private long mHistoryBufferStartTime; private final ArraySet<PowerStats.Descriptor> mWrittenPowerStatsDescriptors = new ArraySet<>(); private byte mLastHistoryStepLevel = 0; private boolean mMutable = true; Loading Loading @@ -307,23 +307,26 @@ public class BatteryStatsHistory { * @param maxHistoryBufferSize the most amount of RAM to used for buffering of history steps */ public BatteryStatsHistory(File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) { HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, MonotonicClock monotonicClock) { this(Parcel.obtain(), systemDir, maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator, clock, new TraceDelegate()); stepDetailsCalculator, clock, monotonicClock, new TraceDelegate()); initHistoryBuffer(); } @VisibleForTesting public BatteryStatsHistory(Parcel historyBuffer, File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer) { HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, MonotonicClock monotonicClock, TraceDelegate tracer) { this(historyBuffer, systemDir, maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator, clock, tracer, null); clock, monotonicClock, tracer, null); } private BatteryStatsHistory(Parcel historyBuffer, File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, MonotonicClock monotonicClock, TraceDelegate tracer, BatteryStatsHistory writableHistory) { mHistoryBuffer = historyBuffer; mSystemDir = systemDir; Loading @@ -332,6 +335,7 @@ public class BatteryStatsHistory { mStepDetailsCalculator = stepDetailsCalculator; mTracer = tracer; mClock = clock; mMonotonicClock = monotonicClock; mWritableHistory = writableHistory; if (mWritableHistory != null) { mMutable = false; Loading Loading @@ -381,16 +385,18 @@ public class BatteryStatsHistory { } private BatteryHistoryFile makeBatteryHistoryFile() { return new BatteryHistoryFile(mHistoryDir, mClock.elapsedRealtime() + mHistoryBaseTimeMs); return new BatteryHistoryFile(mHistoryDir, mMonotonicClock.monotonicTime()); } public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) { HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, MonotonicClock monotonicClock) { mMaxHistoryFiles = maxHistoryFiles; mMaxHistoryBufferSize = maxHistoryBufferSize; mStepDetailsCalculator = stepDetailsCalculator; mTracer = new TraceDelegate(); mClock = clock; mMonotonicClock = monotonicClock; mHistoryBuffer = Parcel.obtain(); mSystemDir = null; Loading @@ -417,16 +423,16 @@ public class BatteryStatsHistory { mHistoryBuffer = Parcel.obtain(); mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length); mMonotonicClock = null; readFromParcel(parcel, true /* useBlobs */); } private void initHistoryBuffer() { mHistoryBaseTimeMs = 0; mLastHistoryElapsedRealtimeMs = 0; mTrackRunningHistoryElapsedRealtimeMs = 0; mTrackRunningHistoryUptimeMs = 0; mWrittenPowerStatsDescriptors.clear(); mHistoryBufferStartTime = mMonotonicClock.monotonicTime(); mHistoryBuffer.setDataSize(0); mHistoryBuffer.setDataPosition(0); mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2); Loading Loading @@ -466,7 +472,7 @@ public class BatteryStatsHistory { historyBufferCopy.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize()); return new BatteryStatsHistory(historyBufferCopy, mSystemDir, 0, 0, null, null, null, this); null, this); } } Loading @@ -491,7 +497,7 @@ public class BatteryStatsHistory { * When {@link #mHistoryBuffer} reaches {@link BatteryStatsImpl.Constants#MAX_HISTORY_BUFFER}, * create next history file. */ public void startNextFile() { public void startNextFile(long elapsedRealtimeMs) { if (mMaxHistoryFiles == 0) { Slog.wtf(TAG, "mMaxHistoryFiles should not be zero when writing history"); return; Loading @@ -517,6 +523,7 @@ public class BatteryStatsHistory { Slog.e(TAG, "Could not create history file: " + mActiveFile.getBaseFile()); } mHistoryBufferStartTime = mMonotonicClock.monotonicTime(elapsedRealtimeMs); mHistoryBuffer.setDataSize(0); mHistoryBuffer.setDataPosition(0); mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2); Loading Loading @@ -699,9 +706,12 @@ public class BatteryStatsHistory { if (mHistoryParcels != null) { while (mParcelIndex < mHistoryParcels.size()) { final Parcel p = mHistoryParcels.get(mParcelIndex++); if (!skipHead(p)) { if (!verifyVersion(p)) { continue; } // skip monotonic time field. p.readLong(); final int bufSize = p.readInt(); final int curPos = p.dataPosition(); mCurrentParcelEnd = curPos + bufSize; Loading Loading @@ -745,24 +755,36 @@ public class BatteryStatsHistory { } out.unmarshall(raw, 0, raw.length); out.setDataPosition(0); return skipHead(out); if (!verifyVersion(out)) { return false; } // skip monotonic time field. out.readLong(); return true; } /** * Skip the header part of history parcel. * Verify header part of history parcel. * * @param p history parcel to skip head. * @return true if version match, false if not. */ private boolean skipHead(Parcel p) { private boolean verifyVersion(Parcel p) { p.setDataPosition(0); final int version = p.readInt(); if (version != VERSION) { return false; return version == VERSION; } // skip historyBaseTime field. p.readLong(); return true; /** * Extracts the monotonic time, as per {@link MonotonicClock}, from the supplied battery history * buffer. */ public long getHistoryBufferStartTime(Parcel p) { int pos = p.dataPosition(); p.setDataPosition(0); p.readInt(); // Skip the version field long monotonicTime = p.readLong(); p.setDataPosition(pos); return monotonicTime; } /** Loading Loading @@ -1438,7 +1460,8 @@ public class BatteryStatsHistory { throw new ConcurrentModificationException("Battery history is not writable"); } final long timeDiffMs = (mHistoryBaseTimeMs + elapsedRealtimeMs) - mHistoryLastWritten.time; final long timeDiffMs = mMonotonicClock.monotonicTime(elapsedRealtimeMs) - mHistoryLastWritten.time; final int diffStates = mHistoryLastWritten.states ^ cur.states; final int diffStates2 = mHistoryLastWritten.states2 ^ cur.states2; final int lastDiffStates = mHistoryLastWritten.states ^ mHistoryLastLastWritten.states; Loading Loading @@ -1476,7 +1499,9 @@ public class BatteryStatsHistory { mHistoryBuffer.setDataSize(mHistoryBufferLastPos); mHistoryBuffer.setDataPosition(mHistoryBufferLastPos); mHistoryBufferLastPos = -1; elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTimeMs; elapsedRealtimeMs -= timeDiffMs; // If the last written history had a wakelock tag, we need to retain it. // Note that the condition above made sure that we aren't in a case where // both it and the current history item have a wakelock tag. Loading Loading @@ -1513,7 +1538,7 @@ public class BatteryStatsHistory { HistoryItem copy = new HistoryItem(); copy.setTo(cur); startNextFile(); startNextFile(elapsedRealtimeMs); // startRecordingHistory will reset mHistoryCur. startRecordingHistory(elapsedRealtimeMs, uptimeMs, false); Loading Loading @@ -1548,7 +1573,7 @@ public class BatteryStatsHistory { mHistoryBufferLastPos = mHistoryBuffer.dataPosition(); mHistoryLastLastWritten.setTo(mHistoryLastWritten); final boolean hasTags = mHistoryLastWritten.tagsFirstOccurrence || cur.tagsFirstOccurrence; mHistoryLastWritten.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur); mHistoryLastWritten.setTo(mMonotonicClock.monotonicTime(elapsedRealtimeMs), cmd, cur); if (mHistoryLastWritten.time < mHistoryLastLastWritten.time - 60000) { Slog.wtf(TAG, "Significantly earlier event written to battery history:" + " time=" + mHistoryLastWritten.time Loading @@ -1556,7 +1581,6 @@ public class BatteryStatsHistory { } mHistoryLastWritten.tagsFirstOccurrence = hasTags; writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten); mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs; cur.wakelockTag = null; cur.wakeReasonTag = null; cur.eventCode = HistoryItem.EVENT_NONE; Loading Loading @@ -1926,6 +1950,10 @@ public class BatteryStatsHistory { return; } // Save the monotonic time first, so that even if the history write below fails, // we still wouldn't end up with overlapping history timelines. mMonotonicClock.write(); Parcel p = Parcel.obtain(); try { final long start = SystemClock.uptimeMillis(); Loading @@ -1951,8 +1979,7 @@ public class BatteryStatsHistory { return; } final long historyBaseTime = in.readLong(); mHistoryBufferStartTime = in.readLong(); mHistoryBuffer.setDataSize(0); mHistoryBuffer.setDataPosition(0); Loading @@ -1972,39 +1999,11 @@ public class BatteryStatsHistory { mHistoryBuffer.appendFrom(in, curPos, bufSize); in.setDataPosition(curPos + bufSize); } mHistoryBaseTimeMs = historyBaseTime; if (DEBUG) { StringBuilder sb = new StringBuilder(128); sb.append("****************** NEW mHistoryBaseTimeMs: "); TimeUtils.formatDuration(mHistoryBaseTimeMs, sb); Slog.i(TAG, sb.toString()); } if (mHistoryBaseTimeMs > 0) { long elapsedRealtimeMs = mClock.elapsedRealtime(); mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs; mHistoryBaseTimeMs = mHistoryBaseTimeMs - elapsedRealtimeMs + 1; if (DEBUG) { StringBuilder sb = new StringBuilder(128); sb.append("****************** ADJUSTED mHistoryBaseTimeMs: "); TimeUtils.formatDuration(mHistoryBaseTimeMs, sb); Slog.i(TAG, sb.toString()); } } } private void writeHistoryBuffer(Parcel out) { if (DEBUG) { StringBuilder sb = new StringBuilder(128); sb.append("****************** WRITING mHistoryBaseTimeMs: "); TimeUtils.formatDuration(mHistoryBaseTimeMs, sb); sb.append(" mLastHistoryElapsedRealtimeMs: "); TimeUtils.formatDuration(mLastHistoryElapsedRealtimeMs, sb); Slog.i(TAG, sb.toString()); } out.writeInt(BatteryStatsHistory.VERSION); out.writeLong(mHistoryBaseTimeMs + mLastHistoryElapsedRealtimeMs); out.writeLong(mHistoryBufferStartTime); out.writeInt(mHistoryBuffer.dataSize()); if (DEBUG) { Slog.i(TAG, "***************** WRITING HISTORY: " Loading
core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +15 −10 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.internal.os; import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import android.os.BatteryManager; import android.os.BatteryStats; Loading @@ -34,8 +33,8 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor private static final boolean DEBUG = false; private static final String TAG = "BatteryStatsHistoryItr"; private final BatteryStatsHistory mBatteryStatsHistory; private final @CurrentTimeMillisLong long mStartTimeMs; private final @CurrentTimeMillisLong long mEndTimeMs; private final long mStartTimeMs; private final long mEndTimeMs; private final BatteryStats.HistoryStepDetails mReadHistoryStepDetails = new BatteryStats.HistoryStepDetails(); private final SparseArray<BatteryStats.HistoryTag> mHistoryTags = new SparseArray<>(); Loading @@ -43,10 +42,10 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor new PowerStats.DescriptorRegistry(); private BatteryStats.HistoryItem mHistoryItem = new BatteryStats.HistoryItem(); private boolean mNextItemReady; private boolean mTimeInitialized; public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, @CurrentTimeMillisLong long startTimeMs, @CurrentTimeMillisLong long endTimeMs) { public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs, long endTimeMs) { mBatteryStatsHistory = history; mStartTimeMs = startTimeMs; mEndTimeMs = (endTimeMs != 0) ? endTimeMs : Long.MAX_VALUE; Loading Loading @@ -82,7 +81,12 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor break; } final long lastRealtimeMs = mHistoryItem.time; if (!mTimeInitialized) { mHistoryItem.time = mBatteryStatsHistory.getHistoryBufferStartTime(p); mTimeInitialized = true; } final long lastMonotonicTimeMs = mHistoryItem.time; final long lastWalltimeMs = mHistoryItem.currentTime; try { readHistoryDelta(p, mHistoryItem); Loading @@ -93,12 +97,13 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor if (mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME && mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_RESET && lastWalltimeMs != 0) { mHistoryItem.currentTime = lastWalltimeMs + (mHistoryItem.time - lastRealtimeMs); mHistoryItem.currentTime = lastWalltimeMs + (mHistoryItem.time - lastMonotonicTimeMs); } if (mEndTimeMs != 0 && mHistoryItem.currentTime >= mEndTimeMs) { if (mEndTimeMs != 0 && mHistoryItem.time >= mEndTimeMs) { break; } if (mHistoryItem.currentTime >= mStartTimeMs) { if (mHistoryItem.time >= mStartTimeMs) { mNextItemReady = true; return; } Loading
core/java/com/android/internal/os/MonotonicClock.java 0 → 100644 +145 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.util.AtomicFile; import android.util.Log; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; /** * A clock that is similar to SystemClock#elapsedRealtime(), except that it is not reset * on reboot, but keeps going. */ public class MonotonicClock { private static final String TAG = "MonotonicClock"; private static final String XML_TAG_MONOTONIC_TIME = "monotonic_time"; private static final String XML_ATTR_TIMESHIFT = "timeshift"; private final AtomicFile mFile; private final Clock mClock; private long mTimeshift; public MonotonicClock(File file) { mFile = new AtomicFile(file); mClock = Clock.SYSTEM_CLOCK; read(); } public MonotonicClock(long monotonicTime, @NonNull Clock clock) { mClock = clock; mFile = null; mTimeshift = monotonicTime - mClock.elapsedRealtime(); } /** * Returns time in milliseconds, based on SystemClock.elapsedTime, adjusted so that * after a device reboot the time keeps increasing. */ public long monotonicTime() { return monotonicTime(mClock.elapsedRealtime()); } /** * Like {@link #monotonicTime()}, except the elapsed time is supplied as an argument instead * of being read from the Clock. */ public long monotonicTime(long elapsedRealtimeMs) { return mTimeshift + elapsedRealtimeMs; } private void read() { if (!mFile.exists()) { return; } try { readXml(new ByteArrayInputStream(mFile.readFully()), Xml.newBinaryPullParser()); } catch (IOException e) { Log.e(TAG, "Cannot load monotonic clock from " + mFile.getBaseFile(), e); } } /** * Saves the timeshift into a file. Call this method just before system shutdown, after * writing the last battery history event. */ public void write() { if (mFile == null) { return; } mFile.write(out -> { try { writeXml(out, Xml.newBinarySerializer()); out.close(); } catch (IOException e) { Log.e(TAG, "Cannot write monotonic clock to " + mFile.getBaseFile(), e); } }); } /** * Parses an XML file containing the persistent state of the monotonic clock. */ @VisibleForTesting public void readXml(InputStream inputStream, TypedXmlPullParser parser) throws IOException { long savedTimeshift = 0; try { parser.setInput(inputStream, StandardCharsets.UTF_8.name()); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG && parser.getName().equals(XML_TAG_MONOTONIC_TIME)) { savedTimeshift = parser.getAttributeLong(null, XML_ATTR_TIMESHIFT); } eventType = parser.next(); } } catch (XmlPullParserException e) { throw new IOException(e); } mTimeshift = savedTimeshift - mClock.elapsedRealtime(); } /** * Creates an XML file containing the persistent state of the monotonic clock. */ @VisibleForTesting public void writeXml(OutputStream out, TypedXmlSerializer serializer) throws IOException { serializer.setOutput(out, StandardCharsets.UTF_8.name()); serializer.startDocument(null, true); serializer.startTag(null, XML_TAG_MONOTONIC_TIME); serializer.attributeLong(null, XML_ATTR_TIMESHIFT, monotonicTime()); serializer.endTag(null, XML_TAG_MONOTONIC_TIME); serializer.endDocument(); } }
core/java/com/android/internal/os/OWNERS +2 −6 Original line number Diff line number Diff line Loading @@ -5,14 +5,10 @@ per-file *Binder* = file:BINDER_OWNERS per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS # BatteryStats per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS per-file BatteryStats* = file:/BATTERY_STATS_OWNERS per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS per-file *ChargeCalculator* = file:/BATTERY_STATS_OWNERS per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS per-file *PowerStats* = file:/BATTERY_STATS_OWNERS per-file *Kernel* = file:/BATTERY_STATS_OWNERS per-file *Clock* = file:/BATTERY_STATS_OWNERS per-file *MultiState* = file:/BATTERY_STATS_OWNERS per-file *PowerProfile* = file:/BATTERY_STATS_OWNERS per-file *PowerStats* = file:/BATTERY_STATS_OWNERS