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