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