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 
28 /**
29  * An iterator for {@link BatteryStats.HistoryItem}'s.
30  */
31 @android.ravenwood.annotation.RavenwoodKeepWholeClass
32 public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.HistoryItem>,
33         AutoCloseable {
34     private static final boolean DEBUG = false;
35     private static final String TAG = "BatteryStatsHistoryItr";
36     private final BatteryStatsHistory mBatteryStatsHistory;
37     private final long mStartTimeMs;
38     private final long mEndTimeMs;
39     private final BatteryStats.HistoryStepDetails mReadHistoryStepDetails =
40             new BatteryStats.HistoryStepDetails();
41     private final SparseArray<BatteryStats.HistoryTag> mHistoryTags = new SparseArray<>();
42     private final PowerStats.DescriptorRegistry mDescriptorRegistry =
43             new PowerStats.DescriptorRegistry();
44     private BatteryStats.HistoryItem mHistoryItem = new BatteryStats.HistoryItem();
45     private boolean mNextItemReady;
46     private boolean mTimeInitialized;
47     private boolean mClosed;
48 
BatteryStatsHistoryIterator(@onNull BatteryStatsHistory history, long startTimeMs, long endTimeMs)49     public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs,
50             long endTimeMs) {
51         mBatteryStatsHistory = history;
52         mStartTimeMs = startTimeMs;
53         mEndTimeMs = (endTimeMs != MonotonicClock.UNDEFINED) ? endTimeMs : Long.MAX_VALUE;
54         mHistoryItem.clear();
55     }
56 
57     @Override
hasNext()58     public boolean hasNext() {
59         if (!mNextItemReady) {
60             advance();
61         }
62 
63         return mHistoryItem != null;
64     }
65 
66     /**
67      * Retrieves the next HistoryItem from battery history, if available. Returns null if there
68      * are no more items.
69      */
70     @Override
next()71     public BatteryStats.HistoryItem next() {
72         if (!mNextItemReady) {
73             advance();
74         }
75         mNextItemReady = false;
76         return mHistoryItem;
77     }
78 
advance()79     private void advance() {
80         while (true) {
81             Parcel p = mBatteryStatsHistory.getNextParcel(mStartTimeMs, mEndTimeMs);
82             if (p == null) {
83                 break;
84             }
85 
86             if (!mTimeInitialized) {
87                 mHistoryItem.time = mBatteryStatsHistory.getHistoryBufferStartTime(p);
88                 mTimeInitialized = true;
89             }
90 
91             final long lastMonotonicTimeMs = mHistoryItem.time;
92             final long lastWalltimeMs = mHistoryItem.currentTime;
93             try {
94                 readHistoryDelta(p, mHistoryItem);
95             } catch (Throwable t) {
96                 Slog.wtf(TAG, "Corrupted battery history", t);
97                 break;
98             }
99             if (mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME
100                     && mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_RESET
101                     && lastWalltimeMs != 0) {
102                 mHistoryItem.currentTime =
103                         lastWalltimeMs + (mHistoryItem.time - lastMonotonicTimeMs);
104             }
105             if (mEndTimeMs != 0 && mHistoryItem.time >= mEndTimeMs) {
106                 break;
107             }
108             if (mHistoryItem.time >= mStartTimeMs) {
109                 mNextItemReady = true;
110                 return;
111             }
112         }
113 
114         mHistoryItem = null;
115         mNextItemReady = true;
116         close();
117     }
118 
readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur)119     private void readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur) {
120         int firstToken = src.readInt();
121         int deltaTimeToken = firstToken & BatteryStatsHistory.DELTA_TIME_MASK;
122         cur.cmd = BatteryStats.HistoryItem.CMD_UPDATE;
123         cur.numReadInts = 1;
124         if (DEBUG) {
125             Slog.i(TAG, "READ DELTA: firstToken=0x" + Integer.toHexString(firstToken)
126                     + " deltaTimeToken=" + deltaTimeToken);
127         }
128 
129         if (deltaTimeToken < BatteryStatsHistory.DELTA_TIME_ABS) {
130             cur.time += deltaTimeToken;
131         } else if (deltaTimeToken == BatteryStatsHistory.DELTA_TIME_ABS) {
132             cur.readFromParcel(src);
133             if (DEBUG) Slog.i(TAG, "READ DELTA: ABS time=" + cur.time);
134             return;
135         } else if (deltaTimeToken == BatteryStatsHistory.DELTA_TIME_INT) {
136             int delta = src.readInt();
137             cur.time += delta;
138             cur.numReadInts += 1;
139             if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time);
140         } else {
141             long delta = src.readLong();
142             if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time);
143             cur.time += delta;
144             cur.numReadInts += 2;
145         }
146 
147         final int batteryLevelInt;
148         if ((firstToken & BatteryStatsHistory.DELTA_BATTERY_LEVEL_FLAG) != 0) {
149             batteryLevelInt = src.readInt();
150             readBatteryLevelInt(batteryLevelInt, cur);
151             cur.numReadInts += 1;
152             if (DEBUG) {
153                 Slog.i(TAG, "READ DELTA: batteryToken=0x"
154                         + Integer.toHexString(batteryLevelInt)
155                         + " batteryLevel=" + cur.batteryLevel
156                         + " batteryTemp=" + cur.batteryTemperature
157                         + " batteryVolt=" + (int) cur.batteryVoltage);
158             }
159         } else {
160             batteryLevelInt = 0;
161         }
162 
163         if ((firstToken & BatteryStatsHistory.DELTA_STATE_FLAG) != 0) {
164             int stateInt = src.readInt();
165             cur.states = (firstToken & BatteryStatsHistory.DELTA_STATE_MASK) | (stateInt
166                     & (~BatteryStatsHistory.STATE_BATTERY_MASK));
167             cur.batteryStatus = (byte) ((stateInt >> BatteryStatsHistory.STATE_BATTERY_STATUS_SHIFT)
168                     & BatteryStatsHistory.STATE_BATTERY_STATUS_MASK);
169             cur.batteryHealth = (byte) ((stateInt >> BatteryStatsHistory.STATE_BATTERY_HEALTH_SHIFT)
170                     & BatteryStatsHistory.STATE_BATTERY_HEALTH_MASK);
171             cur.batteryPlugType = (byte) ((stateInt >> BatteryStatsHistory.STATE_BATTERY_PLUG_SHIFT)
172                     & BatteryStatsHistory.STATE_BATTERY_PLUG_MASK);
173             switch (cur.batteryPlugType) {
174                 case 1:
175                     cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_AC;
176                     break;
177                 case 2:
178                     cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_USB;
179                     break;
180                 case 3:
181                     cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
182                     break;
183             }
184             cur.numReadInts += 1;
185             if (DEBUG) {
186                 Slog.i(TAG, "READ DELTA: stateToken=0x"
187                         + Integer.toHexString(stateInt)
188                         + " batteryStatus=" + cur.batteryStatus
189                         + " batteryHealth=" + cur.batteryHealth
190                         + " batteryPlugType=" + cur.batteryPlugType
191                         + " states=0x" + Integer.toHexString(cur.states));
192             }
193         } else {
194             cur.states = (firstToken & BatteryStatsHistory.DELTA_STATE_MASK) | (cur.states
195                     & (~BatteryStatsHistory.STATE_BATTERY_MASK));
196         }
197 
198         if ((firstToken & BatteryStatsHistory.DELTA_STATE2_FLAG) != 0) {
199             cur.states2 = src.readInt();
200             if (DEBUG) {
201                 Slog.i(TAG, "READ DELTA: states2=0x"
202                         + Integer.toHexString(cur.states2));
203             }
204         }
205 
206         if ((firstToken & BatteryStatsHistory.DELTA_WAKELOCK_FLAG) != 0) {
207             final int indexes = src.readInt();
208             final int wakeLockIndex = indexes & 0xffff;
209             final int wakeReasonIndex = (indexes >> 16) & 0xffff;
210             if (readHistoryTag(src, wakeLockIndex, cur.localWakelockTag)) {
211                 cur.wakelockTag = cur.localWakelockTag;
212             } else {
213                 cur.wakelockTag = null;
214             }
215             if (readHistoryTag(src, wakeReasonIndex, cur.localWakeReasonTag)) {
216                 cur.wakeReasonTag = cur.localWakeReasonTag;
217             } else {
218                 cur.wakeReasonTag = null;
219             }
220             cur.numReadInts += 1;
221         } else {
222             cur.wakelockTag = null;
223             cur.wakeReasonTag = null;
224         }
225 
226         if ((firstToken & BatteryStatsHistory.DELTA_EVENT_FLAG) != 0) {
227             cur.eventTag = cur.localEventTag;
228             final int codeAndIndex = src.readInt();
229             cur.eventCode = (codeAndIndex & 0xffff);
230             final int index = ((codeAndIndex >> 16) & 0xffff);
231             if (readHistoryTag(src, index, cur.localEventTag)) {
232                 cur.eventTag = cur.localEventTag;
233             } else {
234                 cur.eventTag = null;
235             }
236             cur.numReadInts += 1;
237             if (DEBUG) {
238                 Slog.i(TAG, "READ DELTA: event=" + cur.eventCode + " tag=#"
239                         + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
240                         + cur.eventTag.string);
241             }
242         } else {
243             cur.eventCode = BatteryStats.HistoryItem.EVENT_NONE;
244         }
245 
246         if ((batteryLevelInt & BatteryStatsHistory.BATTERY_LEVEL_DETAILS_FLAG) != 0) {
247             cur.stepDetails = mReadHistoryStepDetails;
248             cur.stepDetails.readFromParcel(src);
249         } else {
250             cur.stepDetails = null;
251         }
252 
253         if ((firstToken & BatteryStatsHistory.DELTA_BATTERY_CHARGE_FLAG) != 0) {
254             cur.batteryChargeUah = src.readInt();
255         }
256         cur.modemRailChargeMah = src.readDouble();
257         cur.wifiRailChargeMah = src.readDouble();
258         if ((cur.states2 & BatteryStats.HistoryItem.STATE2_EXTENSIONS_FLAG) != 0) {
259             final int extensionFlags = src.readInt();
260             if ((extensionFlags & BatteryStatsHistory.EXTENSION_POWER_STATS_DESCRIPTOR_FLAG) != 0) {
261                 PowerStats.Descriptor descriptor = PowerStats.Descriptor.readSummaryFromParcel(src);
262                 if (descriptor != null) {
263                     mDescriptorRegistry.register(descriptor);
264                 }
265             }
266             if ((extensionFlags & BatteryStatsHistory.EXTENSION_POWER_STATS_FLAG) != 0) {
267                 cur.powerStats = PowerStats.readFromParcel(src, mDescriptorRegistry);
268             } else {
269                 cur.powerStats = null;
270             }
271             if ((extensionFlags & BatteryStatsHistory.EXTENSION_PROCESS_STATE_CHANGE_FLAG) != 0) {
272                 cur.processStateChange = cur.localProcessStateChange;
273                 cur.processStateChange.readFromParcel(src);
274             } else {
275                 cur.processStateChange = null;
276             }
277         } else {
278             cur.powerStats = null;
279             cur.processStateChange = null;
280         }
281     }
282 
readHistoryTag(Parcel src, int index, BatteryStats.HistoryTag outTag)283     private boolean readHistoryTag(Parcel src, int index, BatteryStats.HistoryTag outTag) {
284         if (index == 0xffff) {
285             return false;
286         }
287 
288         if ((index & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
289             BatteryStats.HistoryTag tag = new BatteryStats.HistoryTag();
290             tag.readFromParcel(src);
291             tag.poolIdx = index & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
292             if (tag.poolIdx < BatteryStatsHistory.HISTORY_TAG_INDEX_LIMIT) {
293                 mHistoryTags.put(tag.poolIdx, tag);
294             } else {
295                 tag.poolIdx = BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW;
296             }
297 
298             outTag.setTo(tag);
299         } else {
300             BatteryStats.HistoryTag historyTag = mHistoryTags.get(index);
301             if (historyTag != null) {
302                 outTag.setTo(historyTag);
303             } else {
304                 outTag.string = null;
305                 outTag.uid = 0;
306             }
307             outTag.poolIdx = index;
308         }
309         return true;
310     }
311 
readBatteryLevelInt(int batteryLevelInt, BatteryStats.HistoryItem out)312     private static void readBatteryLevelInt(int batteryLevelInt, BatteryStats.HistoryItem out) {
313         out.batteryLevel = (byte) ((batteryLevelInt & 0xfe000000) >>> 25);
314         out.batteryTemperature = (short) ((batteryLevelInt & 0x01ff8000) >>> 15);
315         int voltage = ((batteryLevelInt & 0x00007ffe) >>> 1);
316         if (voltage == 0x3FFF) {
317             voltage = -1;
318         }
319 
320         out.batteryVoltage = (short) voltage;
321     }
322 
323     /**
324      * Should be called when iteration is complete.
325      */
326     @Override
close()327     public void close() {
328         if (!mClosed) {
329             mClosed = true;
330             mBatteryStatsHistory.iteratorFinished();
331         }
332     }
333 }
334