• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.os;
18 
19 import android.annotation.NonNull;
20 import android.os.BatteryManager;
21 import android.os.BatteryStats;
22 import android.os.Parcel;
23 import android.util.Slog;
24 import android.util.SparseArray;
25 
26 import java.util.Iterator;
27 import java.util.Queue;
28 
29 /**
30  * An iterator for {@link BatteryStats.HistoryItem}'s.
31  */
32 @android.ravenwood.annotation.RavenwoodKeepWholeClass
33 public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.HistoryItem>,
34         AutoCloseable {
35     private static final boolean DEBUG = false;
36     private static final String TAG = "BatteryStatsHistoryItr";
37     private final BatteryStatsHistory mBatteryStatsHistory;
38     private final long mStartTimeMs;
39     private final long mEndTimeMs;
40     private final BatteryStats.HistoryStepDetails mReadHistoryStepDetails =
41             new BatteryStats.HistoryStepDetails();
42     private final SparseArray<BatteryStats.HistoryTag> mHistoryTags = new SparseArray<>();
43     private final PowerStats.DescriptorRegistry mDescriptorRegistry =
44             new PowerStats.DescriptorRegistry();
45     private BatteryStats.HistoryItem mHistoryItem = new BatteryStats.HistoryItem();
46     private boolean mNextItemReady;
47     private boolean mTimeInitialized;
48     private boolean mClosed;
49     private long mBaseMonotonicTime;
50     private long mBaseTimeUtc;
51     private int mItemIndex = 0;
52     private final int mMaxHistoryItems;
53     private int mParcelDataPosition;
54 
55     private Queue<BatteryStatsHistory.BatteryHistoryParcelContainer> mParcelContainers;
56 
BatteryStatsHistoryIterator(@onNull BatteryStatsHistory history, long startTimeMs, long endTimeMs)57     public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs,
58             long endTimeMs) {
59         mBatteryStatsHistory = history;
60         mStartTimeMs = startTimeMs;
61         mEndTimeMs = (endTimeMs != MonotonicClock.UNDEFINED) ? endTimeMs : Long.MAX_VALUE;
62         mHistoryItem.clear();
63         mMaxHistoryItems = history.getEstimatedItemCount();
64     }
65 
66     @Override
hasNext()67     public boolean hasNext() {
68         if (!mNextItemReady) {
69             if (!advance()) {
70                 mHistoryItem = null;
71                 close();
72             }
73             mNextItemReady = true;
74         }
75 
76         return mHistoryItem != null;
77     }
78 
79     /**
80      * Retrieves the next HistoryItem from battery history, if available. Returns null if there
81      * are no more items.
82      */
83     @Override
next()84     public BatteryStats.HistoryItem next() {
85         if (!mNextItemReady) {
86             if (!advance()) {
87                 mHistoryItem = null;
88                 close();
89             }
90         }
91         mNextItemReady = false;
92         return mHistoryItem;
93     }
94 
advance()95     private boolean advance() {
96         if (mParcelContainers == null) {
97             mParcelContainers = mBatteryStatsHistory.getParcelContainers(mStartTimeMs, mEndTimeMs);
98         }
99 
100         BatteryStatsHistory.BatteryHistoryParcelContainer container;
101         while ((container = mParcelContainers.peek()) != null) {
102             Parcel p = container.getParcel();
103             if (p == null || p.dataPosition() >= p.dataSize()) {
104                 container.close();
105                 mParcelContainers.remove();
106                 mParcelDataPosition = 0;
107                 continue;
108             }
109 
110             if (!mTimeInitialized) {
111                 mBaseMonotonicTime = container.getMonotonicStartTime();
112                 mHistoryItem.time = mBaseMonotonicTime;
113                 mTimeInitialized = true;
114             }
115 
116             try {
117                 readHistoryDelta(p, mHistoryItem);
118                 int dataPosition = p.dataPosition();
119                 if (dataPosition <= mParcelDataPosition) {
120                     Slog.wtf(TAG, "Corrupted battery history, parcel is not progressing: "
121                             + dataPosition + " of " + p.dataSize());
122                     return false;
123                 }
124                 mParcelDataPosition = dataPosition;
125             } catch (Throwable t) {
126                 Slog.wtf(TAG, "Corrupted battery history", t);
127                 return false;
128             }
129 
130             if (mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME
131                     || mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_RESET) {
132                 mBaseTimeUtc = mHistoryItem.currentTime - (mHistoryItem.time - mBaseMonotonicTime);
133             }
134 
135             if (mHistoryItem.time < mStartTimeMs) {
136                 continue;
137             }
138 
139             if (mEndTimeMs != 0 && mEndTimeMs != MonotonicClock.UNDEFINED
140                     && mHistoryItem.time >= mEndTimeMs) {
141                 return false;
142             }
143 
144             if (mItemIndex++ > mMaxHistoryItems) {
145                 Slog.wtfStack(TAG, "Number of battery history items is too large: " + mItemIndex);
146                 return false;
147             }
148 
149             mHistoryItem.currentTime = mBaseTimeUtc + (mHistoryItem.time - mBaseMonotonicTime);
150             return true;
151         }
152         return false;
153     }
154 
readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur)155     private void readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur) {
156         int firstToken = src.readInt();
157         int deltaTimeToken = firstToken & BatteryStatsHistory.DELTA_TIME_MASK;
158         cur.cmd = BatteryStats.HistoryItem.CMD_UPDATE;
159         cur.numReadInts = 1;
160         if (DEBUG) {
161             Slog.i(TAG, "READ DELTA: firstToken=0x" + Integer.toHexString(firstToken)
162                     + " deltaTimeToken=" + deltaTimeToken);
163         }
164 
165         if (deltaTimeToken < BatteryStatsHistory.DELTA_TIME_ABS) {
166             cur.time += deltaTimeToken;
167         } else if (deltaTimeToken == BatteryStatsHistory.DELTA_TIME_ABS) {
168             cur.readFromParcel(src);
169             if (DEBUG) Slog.i(TAG, "READ DELTA: ABS time=" + cur.time);
170             return;
171         } else if (deltaTimeToken == BatteryStatsHistory.DELTA_TIME_INT) {
172             int delta = src.readInt();
173             cur.time += delta;
174             cur.numReadInts += 1;
175             if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time);
176         } else {
177             long delta = src.readLong();
178             if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time);
179             cur.time += delta;
180             cur.numReadInts += 2;
181         }
182 
183         final int batteryLevelInt;
184         if ((firstToken & BatteryStatsHistory.DELTA_BATTERY_LEVEL_FLAG) != 0) {
185             batteryLevelInt = src.readInt();
186             cur.numReadInts += 1;
187             final boolean overflow =
188                     (batteryLevelInt & BatteryStatsHistory.BATTERY_LEVEL_OVERFLOW_FLAG) != 0;
189             int extendedBatteryLevelInt = 0;
190             if (overflow) {
191                 extendedBatteryLevelInt = src.readInt();
192                 cur.numReadInts += 1;
193             }
194             readBatteryLevelInts(batteryLevelInt, extendedBatteryLevelInt, cur);
195             if (DEBUG) {
196                 Slog.i(TAG, "READ DELTA: batteryToken=0x"
197                         + Integer.toHexString(batteryLevelInt)
198                         + (overflow
199                                 ? " batteryToken2=0x" + Integer.toHexString(extendedBatteryLevelInt)
200                                 : "")
201                         + " batteryLevel=" + cur.batteryLevel
202                         + " batteryTemp=" + cur.batteryTemperature
203                         + " batteryVolt=" + (int) cur.batteryVoltage);
204             }
205         } else {
206             batteryLevelInt = 0;
207         }
208 
209         if ((firstToken & BatteryStatsHistory.DELTA_STATE_FLAG) != 0) {
210             int stateInt = src.readInt();
211             cur.states = (firstToken & BatteryStatsHistory.DELTA_STATE_MASK) | (stateInt
212                     & (~BatteryStatsHistory.STATE_BATTERY_MASK));
213             cur.batteryStatus = (byte) ((stateInt >> BatteryStatsHistory.STATE_BATTERY_STATUS_SHIFT)
214                     & BatteryStatsHistory.STATE_BATTERY_STATUS_MASK);
215             cur.batteryHealth = (byte) ((stateInt >> BatteryStatsHistory.STATE_BATTERY_HEALTH_SHIFT)
216                     & BatteryStatsHistory.STATE_BATTERY_HEALTH_MASK);
217             cur.batteryPlugType = (byte) ((stateInt >> BatteryStatsHistory.STATE_BATTERY_PLUG_SHIFT)
218                     & BatteryStatsHistory.STATE_BATTERY_PLUG_MASK);
219             switch (cur.batteryPlugType) {
220                 case 1:
221                     cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_AC;
222                     break;
223                 case 2:
224                     cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_USB;
225                     break;
226                 case 3:
227                     cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
228                     break;
229             }
230             cur.numReadInts += 1;
231             if (DEBUG) {
232                 Slog.i(TAG, "READ DELTA: stateToken=0x"
233                         + Integer.toHexString(stateInt)
234                         + " batteryStatus=" + cur.batteryStatus
235                         + " batteryHealth=" + cur.batteryHealth
236                         + " batteryPlugType=" + cur.batteryPlugType
237                         + " states=0x" + Integer.toHexString(cur.states));
238             }
239         } else {
240             cur.states = (firstToken & BatteryStatsHistory.DELTA_STATE_MASK) | (cur.states
241                     & (~BatteryStatsHistory.STATE_BATTERY_MASK));
242         }
243 
244         if ((firstToken & BatteryStatsHistory.DELTA_STATE2_FLAG) != 0) {
245             cur.states2 = src.readInt();
246             if (DEBUG) {
247                 Slog.i(TAG, "READ DELTA: states2=0x"
248                         + Integer.toHexString(cur.states2));
249             }
250         }
251 
252         if ((firstToken & BatteryStatsHistory.DELTA_WAKELOCK_FLAG) != 0) {
253             final int indexes = src.readInt();
254             final int wakeLockIndex = indexes & 0xffff;
255             final int wakeReasonIndex = (indexes >> 16) & 0xffff;
256             if (readHistoryTag(src, wakeLockIndex, cur.localWakelockTag)) {
257                 cur.wakelockTag = cur.localWakelockTag;
258             } else {
259                 cur.wakelockTag = null;
260             }
261             if (readHistoryTag(src, wakeReasonIndex, cur.localWakeReasonTag)) {
262                 cur.wakeReasonTag = cur.localWakeReasonTag;
263             } else {
264                 cur.wakeReasonTag = null;
265             }
266             cur.numReadInts += 1;
267         } else {
268             cur.wakelockTag = null;
269             cur.wakeReasonTag = null;
270         }
271 
272         if ((firstToken & BatteryStatsHistory.DELTA_EVENT_FLAG) != 0) {
273             cur.eventTag = cur.localEventTag;
274             final int codeAndIndex = src.readInt();
275             cur.eventCode = (codeAndIndex & 0xffff);
276             final int index = ((codeAndIndex >> 16) & 0xffff);
277             if (readHistoryTag(src, index, cur.localEventTag)) {
278                 cur.eventTag = cur.localEventTag;
279             } else {
280                 cur.eventTag = null;
281             }
282             cur.numReadInts += 1;
283             if (DEBUG) {
284                 Slog.i(TAG, "READ DELTA: event=" + cur.eventCode + " tag=#"
285                         + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
286                         + cur.eventTag.string);
287             }
288         } else {
289             cur.eventCode = BatteryStats.HistoryItem.EVENT_NONE;
290         }
291 
292         if ((batteryLevelInt & BatteryStatsHistory.BATTERY_LEVEL_DETAILS_FLAG) != 0) {
293             cur.stepDetails = mReadHistoryStepDetails;
294             cur.stepDetails.readFromParcel(src);
295         } else {
296             cur.stepDetails = null;
297         }
298 
299         if ((firstToken & BatteryStatsHistory.DELTA_BATTERY_CHARGE_FLAG) != 0) {
300             cur.batteryChargeUah = src.readInt();
301         }
302         cur.modemRailChargeMah = src.readDouble();
303         cur.wifiRailChargeMah = src.readDouble();
304         if ((cur.states2 & BatteryStats.HistoryItem.STATE2_EXTENSIONS_FLAG) != 0) {
305             final int extensionFlags = src.readInt();
306             if ((extensionFlags & BatteryStatsHistory.EXTENSION_POWER_STATS_DESCRIPTOR_FLAG) != 0) {
307                 PowerStats.Descriptor descriptor = PowerStats.Descriptor.readSummaryFromParcel(src);
308                 if (descriptor != null) {
309                     mDescriptorRegistry.register(descriptor);
310                 }
311             }
312             if ((extensionFlags & BatteryStatsHistory.EXTENSION_POWER_STATS_FLAG) != 0) {
313                 cur.powerStats = PowerStats.readFromParcel(src, mDescriptorRegistry);
314             } else {
315                 cur.powerStats = null;
316             }
317             if ((extensionFlags & BatteryStatsHistory.EXTENSION_PROCESS_STATE_CHANGE_FLAG) != 0) {
318                 cur.processStateChange = cur.localProcessStateChange;
319                 cur.processStateChange.readFromParcel(src);
320             } else {
321                 cur.processStateChange = null;
322             }
323         } else {
324             cur.powerStats = null;
325             cur.processStateChange = null;
326         }
327     }
328 
readHistoryTag(Parcel src, int index, BatteryStats.HistoryTag outTag)329     private boolean readHistoryTag(Parcel src, int index, BatteryStats.HistoryTag outTag) {
330         if (index == 0xffff) {
331             return false;
332         }
333 
334         if ((index & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
335             BatteryStats.HistoryTag tag = new BatteryStats.HistoryTag();
336             tag.readFromParcel(src);
337             tag.poolIdx = index & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
338             if (tag.poolIdx < BatteryStatsHistory.HISTORY_TAG_INDEX_LIMIT) {
339                 mHistoryTags.put(tag.poolIdx, tag);
340             } else {
341                 tag.poolIdx = BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW;
342             }
343 
344             outTag.setTo(tag);
345         } else {
346             BatteryStats.HistoryTag historyTag = mHistoryTags.get(index);
347             if (historyTag != null) {
348                 outTag.setTo(historyTag);
349             } else {
350                 outTag.string = null;
351                 outTag.uid = 0;
352             }
353             outTag.poolIdx = index;
354         }
355         return true;
356     }
357 
extractSignedBitField(int bits, int mask, int shift)358     private static int extractSignedBitField(int bits, int mask, int shift) {
359         mask >>>= shift;
360         bits >>>= shift;
361         int value = bits & mask;
362         int msbMask = mask ^ (mask >>> 1);
363         // Sign extend with MSB
364         if ((value & msbMask) != 0) value |= ~mask;
365         return value;
366     }
367 
readBatteryLevelInts(int batteryInt, int extendedBatteryInt, BatteryStats.HistoryItem out)368     private static void readBatteryLevelInts(int batteryInt, int extendedBatteryInt,
369             BatteryStats.HistoryItem out) {
370 
371         out.batteryLevel += extractSignedBitField(
372                 batteryInt,
373                 BatteryStatsHistory.BATTERY_LEVEL_LEVEL_MASK,
374                 BatteryStatsHistory.BATTERY_LEVEL_LEVEL_SHIFT);
375 
376         if ((batteryInt & BatteryStatsHistory.BATTERY_LEVEL_OVERFLOW_FLAG) == 0) {
377             out.batteryTemperature += extractSignedBitField(
378                     batteryInt,
379                     BatteryStatsHistory.BATTERY_LEVEL_TEMP_MASK,
380                     BatteryStatsHistory.BATTERY_LEVEL_TEMP_SHIFT);
381             out.batteryVoltage += extractSignedBitField(
382                     batteryInt,
383                     BatteryStatsHistory.BATTERY_LEVEL_VOLT_MASK,
384                     BatteryStatsHistory.BATTERY_LEVEL_VOLT_SHIFT);
385         } else {
386             out.batteryTemperature = (short) extractSignedBitField(
387                     extendedBatteryInt,
388                     BatteryStatsHistory.BATTERY_LEVEL2_TEMP_MASK,
389                     BatteryStatsHistory.BATTERY_LEVEL2_TEMP_SHIFT);
390             out.batteryVoltage = (short) extractSignedBitField(
391                     extendedBatteryInt,
392                     BatteryStatsHistory.BATTERY_LEVEL2_VOLT_MASK,
393                     BatteryStatsHistory.BATTERY_LEVEL2_VOLT_SHIFT);
394         }
395     }
396 
397     /**
398      * Should be called when iteration is complete.
399      */
400     @Override
close()401     public void close() {
402         if (!mClosed) {
403             mClosed = true;
404             mBatteryStatsHistory.iteratorFinished();
405         }
406     }
407 }
408