1 /* 2 * Copyright (C) 2018 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.annotation.Nullable; 21 import android.os.BatteryManager; 22 import android.os.BatteryStats; 23 import android.os.BatteryStats.BitDescription; 24 import android.os.BatteryStats.CpuUsageDetails; 25 import android.os.BatteryStats.EnergyConsumerDetails; 26 import android.os.BatteryStats.HistoryItem; 27 import android.os.BatteryStats.HistoryStepDetails; 28 import android.os.BatteryStats.HistoryTag; 29 import android.os.Build; 30 import android.os.Parcel; 31 import android.os.ParcelFormatException; 32 import android.os.Process; 33 import android.os.StatFs; 34 import android.os.SystemClock; 35 import android.os.SystemProperties; 36 import android.os.Trace; 37 import android.util.ArraySet; 38 import android.util.AtomicFile; 39 import android.util.Slog; 40 import android.util.SparseArray; 41 import android.util.TimeUtils; 42 43 import com.android.internal.annotations.GuardedBy; 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.util.ParseUtils; 46 47 import java.io.File; 48 import java.io.FileOutputStream; 49 import java.io.IOException; 50 import java.util.ArrayList; 51 import java.util.Collections; 52 import java.util.ConcurrentModificationException; 53 import java.util.HashMap; 54 import java.util.List; 55 import java.util.Map; 56 import java.util.Set; 57 import java.util.concurrent.locks.ReentrantLock; 58 59 /** 60 * BatteryStatsHistory encapsulates battery history files. 61 * Battery history record is appended into buffer {@link #mHistoryBuffer} and backed up into 62 * {@link #mActiveFile}. 63 * When {@link #mHistoryBuffer} size reaches {@link BatteryStatsImpl.Constants#MAX_HISTORY_BUFFER}, 64 * current mActiveFile is closed and a new mActiveFile is open. 65 * History files are under directory /data/system/battery-history/. 66 * History files have name battery-history-<num>.bin. The file number <num> starts from zero and 67 * grows sequentially. 68 * The mActiveFile is always the highest numbered history file. 69 * The lowest number file is always the oldest file. 70 * The highest number file is always the newest file. 71 * The file number grows sequentially and we never skip number. 72 * When count of history files exceeds {@link BatteryStatsImpl.Constants#MAX_HISTORY_FILES}, 73 * the lowest numbered file is deleted and a new file is open. 74 * 75 * All interfaces in BatteryStatsHistory should only be called by BatteryStatsImpl and protected by 76 * locks on BatteryStatsImpl object. 77 */ 78 public class BatteryStatsHistory { 79 private static final boolean DEBUG = false; 80 private static final String TAG = "BatteryStatsHistory"; 81 82 // Current on-disk Parcel version. Must be updated when the format of the parcelable changes 83 private static final int VERSION = 209; 84 85 private static final String HISTORY_DIR = "battery-history"; 86 private static final String FILE_SUFFIX = ".bin"; 87 private static final int MIN_FREE_SPACE = 100 * 1024 * 1024; 88 89 // Part of initial delta int that specifies the time delta. 90 static final int DELTA_TIME_MASK = 0x7ffff; 91 static final int DELTA_TIME_LONG = 0x7ffff; // The delta is a following long 92 static final int DELTA_TIME_INT = 0x7fffe; // The delta is a following int 93 static final int DELTA_TIME_ABS = 0x7fffd; // Following is an entire abs update. 94 // Flag in delta int: a new battery level int follows. 95 static final int DELTA_BATTERY_LEVEL_FLAG = 0x00080000; 96 // Flag in delta int: a new full state and battery status int follows. 97 static final int DELTA_STATE_FLAG = 0x00100000; 98 // Flag in delta int: a new full state2 int follows. 99 static final int DELTA_STATE2_FLAG = 0x00200000; 100 // Flag in delta int: contains a wakelock or wakeReason tag. 101 static final int DELTA_WAKELOCK_FLAG = 0x00400000; 102 // Flag in delta int: contains an event description. 103 static final int DELTA_EVENT_FLAG = 0x00800000; 104 // Flag in delta int: contains the battery charge count in uAh. 105 static final int DELTA_BATTERY_CHARGE_FLAG = 0x01000000; 106 // These upper bits are the frequently changing state bits. 107 static final int DELTA_STATE_MASK = 0xfe000000; 108 // These are the pieces of battery state that are packed in to the upper bits of 109 // the state int that have been packed in to the first delta int. They must fit 110 // in STATE_BATTERY_MASK. 111 static final int STATE_BATTERY_MASK = 0xff000000; 112 static final int STATE_BATTERY_STATUS_MASK = 0x00000007; 113 static final int STATE_BATTERY_STATUS_SHIFT = 29; 114 static final int STATE_BATTERY_HEALTH_MASK = 0x00000007; 115 static final int STATE_BATTERY_HEALTH_SHIFT = 26; 116 static final int STATE_BATTERY_PLUG_MASK = 0x00000003; 117 static final int STATE_BATTERY_PLUG_SHIFT = 24; 118 119 // We use the low bit of the battery state int to indicate that we have full details 120 // from a battery level change. 121 static final int BATTERY_LEVEL_DETAILS_FLAG = 0x00000001; 122 123 // Flag in history tag index: indicates that this is the first occurrence of this tag, 124 // therefore the tag value is written in the parcel 125 static final int TAG_FIRST_OCCURRENCE_FLAG = 0x8000; 126 127 static final int EXTENSION_MEASURED_ENERGY_HEADER_FLAG = 0x00000001; 128 static final int EXTENSION_MEASURED_ENERGY_FLAG = 0x00000002; 129 static final int EXTENSION_CPU_USAGE_HEADER_FLAG = 0x00000004; 130 static final int EXTENSION_CPU_USAGE_FLAG = 0x00000008; 131 132 private final Parcel mHistoryBuffer; 133 private final File mSystemDir; 134 private final HistoryStepDetailsCalculator mStepDetailsCalculator; 135 private final File mHistoryDir; 136 private final Clock mClock; 137 138 private int mMaxHistoryFiles; 139 private int mMaxHistoryBufferSize; 140 141 /** 142 * The active history file that the history buffer is backed up into. 143 */ 144 private AtomicFile mActiveFile; 145 /** 146 * A list of history files with incremental indexes. 147 */ 148 private final List<Integer> mFileNumbers = new ArrayList<>(); 149 150 /** 151 * A list of small history parcels, used when BatteryStatsImpl object is created from 152 * deserialization of a parcel, such as Settings app or checkin file. 153 */ 154 private List<Parcel> mHistoryParcels = null; 155 156 /** 157 * When iterating history files, the current file index. 158 */ 159 private int mCurrentFileIndex; 160 /** 161 * When iterating history files, the current file parcel. 162 */ 163 private Parcel mCurrentParcel; 164 /** 165 * When iterating history file, the current parcel's Parcel.dataSize(). 166 */ 167 private int mCurrentParcelEnd; 168 /** 169 * Used when BatteryStatsImpl object is created from deserialization of a parcel, 170 * such as Settings app or checkin file, to iterate over history parcels. 171 */ 172 private int mParcelIndex = 0; 173 174 private final ReentrantLock mWriteLock = new ReentrantLock(); 175 176 private final HistoryItem mHistoryCur = new HistoryItem(); 177 178 private boolean mHaveBatteryLevel; 179 private boolean mRecordingHistory; 180 181 static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe; 182 private static final int MAX_HISTORY_TAG_STRING_LENGTH = 1024; 183 184 private final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>(); 185 private SparseArray<HistoryTag> mHistoryTags; 186 private final HistoryItem mHistoryLastWritten = new HistoryItem(); 187 private final HistoryItem mHistoryLastLastWritten = new HistoryItem(); 188 private final HistoryItem mHistoryAddTmp = new HistoryItem(); 189 private int mNextHistoryTagIdx = 0; 190 private int mNumHistoryTagChars = 0; 191 private int mHistoryBufferLastPos = -1; 192 private long mLastHistoryElapsedRealtimeMs = 0; 193 private long mTrackRunningHistoryElapsedRealtimeMs = 0; 194 private long mTrackRunningHistoryUptimeMs = 0; 195 private long mHistoryBaseTimeMs; 196 private boolean mMeasuredEnergyHeaderWritten = false; 197 private boolean mCpuUsageHeaderWritten = false; 198 private final VarintParceler mVarintParceler = new VarintParceler(); 199 private byte mLastHistoryStepLevel = 0; 200 private boolean mMutable = true; 201 private final BatteryStatsHistory mWritableHistory; 202 private boolean mCleanupEnabled = true; 203 204 /** 205 * A delegate responsible for computing additional details for a step in battery history. 206 */ 207 public interface HistoryStepDetailsCalculator { 208 /** 209 * Returns additional details for the current history step or null. 210 */ 211 @Nullable getHistoryStepDetails()212 HistoryStepDetails getHistoryStepDetails(); 213 214 /** 215 * Resets the calculator to get ready for a new battery session 216 */ clear()217 void clear(); 218 } 219 220 /** 221 * A delegate for android.os.Trace to allow testing static calls. Due to 222 * limitations in Android Tracing (b/153319140), the delegate also records 223 * counter values in system properties which allows reading the value at the 224 * start of a tracing session. This overhead is limited to userdebug builds. 225 * On user builds, tracing still occurs but the counter value will be missing 226 * until the first change occurs. 227 */ 228 @VisibleForTesting 229 public static class TraceDelegate { 230 // Note: certain tests currently run as platform_app which is not allowed 231 // to set debug system properties. To ensure that system properties are set 232 // only when allowed, we check the current UID. 233 private final boolean mShouldSetProperty = 234 Build.IS_USERDEBUG && (Process.myUid() == Process.SYSTEM_UID); 235 236 /** 237 * Returns true if trace counters should be recorded. 238 */ tracingEnabled()239 public boolean tracingEnabled() { 240 return Trace.isTagEnabled(Trace.TRACE_TAG_POWER) || mShouldSetProperty; 241 } 242 243 /** 244 * Records the counter value with the given name. 245 */ traceCounter(@onNull String name, int value)246 public void traceCounter(@NonNull String name, int value) { 247 Trace.traceCounter(Trace.TRACE_TAG_POWER, name, value); 248 if (mShouldSetProperty) { 249 SystemProperties.set("debug.tracing." + name, Integer.toString(value)); 250 } 251 } 252 253 /** 254 * Records an instant event (one with no duration). 255 */ traceInstantEvent(@onNull String track, @NonNull String name)256 public void traceInstantEvent(@NonNull String track, @NonNull String name) { 257 Trace.instantForTrack(Trace.TRACE_TAG_POWER, track, name); 258 } 259 } 260 261 private TraceDelegate mTracer; 262 private int mTraceLastState = 0; 263 private int mTraceLastState2 = 0; 264 265 /** 266 * Constructor 267 * 268 * @param systemDir typically /data/system 269 * @param maxHistoryFiles the largest number of history buffer files to keep 270 * @param maxHistoryBufferSize the most amount of RAM to used for buffering of history steps 271 */ BatteryStatsHistory(File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock)272 public BatteryStatsHistory(File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, 273 HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) { 274 this(Parcel.obtain(), systemDir, maxHistoryFiles, maxHistoryBufferSize, 275 stepDetailsCalculator, clock, new TraceDelegate()); 276 initHistoryBuffer(); 277 } 278 279 @VisibleForTesting BatteryStatsHistory(Parcel historyBuffer, File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer)280 public BatteryStatsHistory(Parcel historyBuffer, File systemDir, 281 int maxHistoryFiles, int maxHistoryBufferSize, 282 HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer) { 283 this(historyBuffer, systemDir, maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator, 284 clock, tracer, null); 285 } 286 BatteryStatsHistory(Parcel historyBuffer, File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer, BatteryStatsHistory writableHistory)287 private BatteryStatsHistory(Parcel historyBuffer, File systemDir, 288 int maxHistoryFiles, int maxHistoryBufferSize, 289 HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer, 290 BatteryStatsHistory writableHistory) { 291 mHistoryBuffer = historyBuffer; 292 mSystemDir = systemDir; 293 mMaxHistoryFiles = maxHistoryFiles; 294 mMaxHistoryBufferSize = maxHistoryBufferSize; 295 mStepDetailsCalculator = stepDetailsCalculator; 296 mTracer = tracer; 297 mClock = clock; 298 mWritableHistory = writableHistory; 299 if (mWritableHistory != null) { 300 mMutable = false; 301 } 302 303 mHistoryDir = new File(systemDir, HISTORY_DIR); 304 mHistoryDir.mkdirs(); 305 if (!mHistoryDir.exists()) { 306 Slog.wtf(TAG, "HistoryDir does not exist:" + mHistoryDir.getPath()); 307 } 308 309 final Set<Integer> dedup = new ArraySet<>(); 310 // scan directory, fill mFileNumbers and mActiveFile. 311 mHistoryDir.listFiles((dir, name) -> { 312 final int b = name.lastIndexOf(FILE_SUFFIX); 313 if (b <= 0) { 314 return false; 315 } 316 final int c = ParseUtils.parseInt(name.substring(0, b), -1); 317 if (c != -1) { 318 dedup.add(c); 319 return true; 320 } else { 321 return false; 322 } 323 }); 324 if (!dedup.isEmpty()) { 325 mFileNumbers.addAll(dedup); 326 Collections.sort(mFileNumbers); 327 setActiveFile(mFileNumbers.get(mFileNumbers.size() - 1)); 328 } else { 329 // No file found, default to have file 0. 330 mFileNumbers.add(0); 331 setActiveFile(0); 332 } 333 } 334 BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock)335 public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize, 336 HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) { 337 mMaxHistoryFiles = maxHistoryFiles; 338 mMaxHistoryBufferSize = maxHistoryBufferSize; 339 mStepDetailsCalculator = stepDetailsCalculator; 340 mTracer = new TraceDelegate(); 341 mClock = clock; 342 343 mHistoryBuffer = Parcel.obtain(); 344 mSystemDir = null; 345 mHistoryDir = null; 346 mWritableHistory = null; 347 initHistoryBuffer(); 348 } 349 350 /** 351 * Used when BatteryStatsHistory object is created from deserialization of a BatteryUsageStats 352 * parcel. 353 */ BatteryStatsHistory(Parcel parcel)354 private BatteryStatsHistory(Parcel parcel) { 355 mClock = Clock.SYSTEM_CLOCK; 356 mTracer = null; 357 mSystemDir = null; 358 mHistoryDir = null; 359 mStepDetailsCalculator = null; 360 mWritableHistory = null; 361 mMutable = false; 362 363 final byte[] historyBlob = parcel.readBlob(); 364 365 mHistoryBuffer = Parcel.obtain(); 366 mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length); 367 368 readFromParcel(parcel, true /* useBlobs */); 369 } 370 initHistoryBuffer()371 private void initHistoryBuffer() { 372 mHistoryBaseTimeMs = 0; 373 mLastHistoryElapsedRealtimeMs = 0; 374 mTrackRunningHistoryElapsedRealtimeMs = 0; 375 mTrackRunningHistoryUptimeMs = 0; 376 mMeasuredEnergyHeaderWritten = false; 377 mCpuUsageHeaderWritten = false; 378 379 mHistoryBuffer.setDataSize(0); 380 mHistoryBuffer.setDataPosition(0); 381 mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2); 382 mHistoryLastLastWritten.clear(); 383 mHistoryLastWritten.clear(); 384 mHistoryTagPool.clear(); 385 mNextHistoryTagIdx = 0; 386 mNumHistoryTagChars = 0; 387 mHistoryBufferLastPos = -1; 388 if (mStepDetailsCalculator != null) { 389 mStepDetailsCalculator.clear(); 390 } 391 } 392 393 /** 394 * Changes the maximum number of history files to be kept. 395 */ setMaxHistoryFiles(int maxHistoryFiles)396 public void setMaxHistoryFiles(int maxHistoryFiles) { 397 mMaxHistoryFiles = maxHistoryFiles; 398 } 399 400 /** 401 * Changes the maximum size of the history buffer, in bytes. 402 */ setMaxHistoryBufferSize(int maxHistoryBufferSize)403 public void setMaxHistoryBufferSize(int maxHistoryBufferSize) { 404 mMaxHistoryBufferSize = maxHistoryBufferSize; 405 } 406 407 /** 408 * Creates a read-only copy of the battery history. Does not copy the files stored 409 * in the system directory, so it is not safe while actively writing history. 410 */ copy()411 public BatteryStatsHistory copy() { 412 synchronized (this) { 413 // Make a copy of battery history to avoid concurrent modification. 414 Parcel historyBufferCopy = Parcel.obtain(); 415 historyBufferCopy.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize()); 416 417 return new BatteryStatsHistory(historyBufferCopy, mSystemDir, 0, 0, null, null, null, 418 this); 419 } 420 } 421 422 /** 423 * Returns true if this instance only supports reading history. 424 */ isReadOnly()425 public boolean isReadOnly() { 426 return mActiveFile == null || mHistoryDir == null; 427 } 428 429 /** 430 * Set the active file that mHistoryBuffer is backed up into. 431 * 432 * @param fileNumber the history file that mHistoryBuffer is backed up into. 433 */ setActiveFile(int fileNumber)434 private void setActiveFile(int fileNumber) { 435 mActiveFile = getFile(fileNumber); 436 if (DEBUG) { 437 Slog.d(TAG, "activeHistoryFile:" + mActiveFile.getBaseFile().getPath()); 438 } 439 } 440 441 /** 442 * Create history AtomicFile from file number. 443 * 444 * @param num file number. 445 * @return AtomicFile object. 446 */ getFile(int num)447 private AtomicFile getFile(int num) { 448 return new AtomicFile( 449 new File(mHistoryDir, num + FILE_SUFFIX)); 450 } 451 452 /** 453 * When {@link #mHistoryBuffer} reaches {@link BatteryStatsImpl.Constants#MAX_HISTORY_BUFFER}, 454 * create next history file. 455 */ startNextFile()456 public void startNextFile() { 457 if (mMaxHistoryFiles == 0) { 458 Slog.wtf(TAG, "mMaxHistoryFiles should not be zero when writing history"); 459 return; 460 } 461 462 if (mFileNumbers.isEmpty()) { 463 Slog.wtf(TAG, "mFileNumbers should never be empty"); 464 return; 465 } 466 467 // The last number in mFileNumbers is the highest number. The next file number is highest 468 // number plus one. 469 final int next = mFileNumbers.get(mFileNumbers.size() - 1) + 1; 470 mFileNumbers.add(next); 471 setActiveFile(next); 472 try { 473 mActiveFile.getBaseFile().createNewFile(); 474 } catch (IOException e) { 475 Slog.e(TAG, "Could not create history file: " + mActiveFile.getBaseFile()); 476 } 477 478 synchronized (this) { 479 cleanupLocked(); 480 } 481 } 482 483 @GuardedBy("this") setCleanupEnabledLocked(boolean enabled)484 private void setCleanupEnabledLocked(boolean enabled) { 485 mCleanupEnabled = enabled; 486 if (mCleanupEnabled) { 487 cleanupLocked(); 488 } 489 } 490 491 @GuardedBy("this") cleanupLocked()492 private void cleanupLocked() { 493 if (!mCleanupEnabled || mHistoryDir == null) { 494 return; 495 } 496 497 // if free disk space is less than 100MB, delete oldest history file. 498 if (!hasFreeDiskSpace()) { 499 int oldest = mFileNumbers.remove(0); 500 getFile(oldest).delete(); 501 } 502 503 // if there are more history files than allowed, delete oldest history files. 504 // mMaxHistoryFiles comes from Constants.MAX_HISTORY_FILES and can be updated by GService 505 // config at run time. 506 while (mFileNumbers.size() > mMaxHistoryFiles) { 507 int oldest = mFileNumbers.get(0); 508 getFile(oldest).delete(); 509 mFileNumbers.remove(0); 510 } 511 } 512 513 /** 514 * Returns true if it is safe to reset history. It will return false if the history is 515 * currently being read. 516 */ isResetEnabled()517 public boolean isResetEnabled() { 518 synchronized (this) { 519 return mCleanupEnabled; 520 } 521 } 522 523 /** 524 * Clear history buffer and delete all existing history files. Active history file start from 525 * number 0 again. 526 */ reset()527 public void reset() { 528 if (DEBUG) Slog.i(TAG, "********** CLEARING HISTORY!"); 529 for (Integer i : mFileNumbers) { 530 getFile(i).delete(); 531 } 532 mFileNumbers.clear(); 533 mFileNumbers.add(0); 534 setActiveFile(0); 535 536 initHistoryBuffer(); 537 } 538 539 /** 540 * Start iterating history files and history buffer. 541 * 542 * @return always return true. 543 */ iterate()544 public BatteryStatsHistoryIterator iterate() { 545 mCurrentFileIndex = 0; 546 mCurrentParcel = null; 547 mCurrentParcelEnd = 0; 548 mParcelIndex = 0; 549 mMutable = false; 550 if (mWritableHistory != null) { 551 synchronized (mWritableHistory) { 552 mWritableHistory.setCleanupEnabledLocked(false); 553 } 554 } 555 return new BatteryStatsHistoryIterator(this); 556 } 557 558 /** 559 * Finish iterating history files and history buffer. 560 */ iteratorFinished()561 void iteratorFinished() { 562 // setDataPosition so mHistoryBuffer Parcel can be written. 563 mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize()); 564 if (mWritableHistory != null) { 565 synchronized (mWritableHistory) { 566 mWritableHistory.setCleanupEnabledLocked(true); 567 } 568 } else { 569 mMutable = true; 570 } 571 } 572 573 /** 574 * When iterating history files and history buffer, always start from the lowest numbered 575 * history file, when reached the mActiveFile (highest numbered history file), do not read from 576 * mActiveFile, read from history buffer instead because the buffer has more updated data. 577 * 578 * @return The parcel that has next record. null if finished all history files and history 579 * buffer 580 */ 581 @Nullable getNextParcel()582 public Parcel getNextParcel() { 583 // First iterate through all records in current parcel. 584 if (mCurrentParcel != null) { 585 if (mCurrentParcel.dataPosition() < mCurrentParcelEnd) { 586 // There are more records in current parcel. 587 return mCurrentParcel; 588 } else if (mHistoryBuffer == mCurrentParcel) { 589 // finished iterate through all history files and history buffer. 590 return null; 591 } else if (mHistoryParcels == null 592 || !mHistoryParcels.contains(mCurrentParcel)) { 593 // current parcel is from history file. 594 mCurrentParcel.recycle(); 595 } 596 } 597 598 // Try next available history file. 599 // skip the last file because its data is in history buffer. 600 while (mCurrentFileIndex < mFileNumbers.size() - 1) { 601 mCurrentParcel = null; 602 mCurrentParcelEnd = 0; 603 final Parcel p = Parcel.obtain(); 604 AtomicFile file = getFile(mFileNumbers.get(mCurrentFileIndex++)); 605 if (readFileToParcel(p, file)) { 606 int bufSize = p.readInt(); 607 int curPos = p.dataPosition(); 608 mCurrentParcelEnd = curPos + bufSize; 609 mCurrentParcel = p; 610 if (curPos < mCurrentParcelEnd) { 611 return mCurrentParcel; 612 } 613 } else { 614 p.recycle(); 615 } 616 } 617 618 // mHistoryParcels is created when BatteryStatsImpl object is created from deserialization 619 // of a parcel, such as Settings app or checkin file. 620 if (mHistoryParcels != null) { 621 while (mParcelIndex < mHistoryParcels.size()) { 622 final Parcel p = mHistoryParcels.get(mParcelIndex++); 623 if (!skipHead(p)) { 624 continue; 625 } 626 final int bufSize = p.readInt(); 627 final int curPos = p.dataPosition(); 628 mCurrentParcelEnd = curPos + bufSize; 629 mCurrentParcel = p; 630 if (curPos < mCurrentParcelEnd) { 631 return mCurrentParcel; 632 } 633 } 634 } 635 636 // finished iterator through history files (except the last one), now history buffer. 637 if (mHistoryBuffer.dataSize() <= 0) { 638 // buffer is empty. 639 return null; 640 } 641 mHistoryBuffer.setDataPosition(0); 642 mCurrentParcel = mHistoryBuffer; 643 mCurrentParcelEnd = mCurrentParcel.dataSize(); 644 return mCurrentParcel; 645 } 646 647 /** 648 * Read history file into a parcel. 649 * 650 * @param out the Parcel read into. 651 * @param file the File to read from. 652 * @return true if success, false otherwise. 653 */ readFileToParcel(Parcel out, AtomicFile file)654 public boolean readFileToParcel(Parcel out, AtomicFile file) { 655 byte[] raw = null; 656 try { 657 final long start = SystemClock.uptimeMillis(); 658 raw = file.readFully(); 659 if (DEBUG) { 660 Slog.d(TAG, "readFileToParcel:" + file.getBaseFile().getPath() 661 + " duration ms:" + (SystemClock.uptimeMillis() - start)); 662 } 663 } catch (Exception e) { 664 Slog.e(TAG, "Error reading file " + file.getBaseFile().getPath(), e); 665 return false; 666 } 667 out.unmarshall(raw, 0, raw.length); 668 out.setDataPosition(0); 669 return skipHead(out); 670 } 671 672 /** 673 * Skip the header part of history parcel. 674 * 675 * @param p history parcel to skip head. 676 * @return true if version match, false if not. 677 */ skipHead(Parcel p)678 private boolean skipHead(Parcel p) { 679 p.setDataPosition(0); 680 final int version = p.readInt(); 681 if (version != VERSION) { 682 return false; 683 } 684 // skip historyBaseTime field. 685 p.readLong(); 686 return true; 687 } 688 689 /** 690 * Writes the battery history contents for persistence. 691 */ writeSummaryToParcel(Parcel out, boolean inclHistory)692 public void writeSummaryToParcel(Parcel out, boolean inclHistory) { 693 out.writeBoolean(inclHistory); 694 if (inclHistory) { 695 writeToParcel(out); 696 } 697 698 out.writeInt(mHistoryTagPool.size()); 699 for (Map.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) { 700 HistoryTag tag = ent.getKey(); 701 out.writeInt(ent.getValue()); 702 out.writeString(tag.string); 703 out.writeInt(tag.uid); 704 } 705 } 706 707 /** 708 * Reads battery history contents from a persisted parcel. 709 */ readSummaryFromParcel(Parcel in)710 public void readSummaryFromParcel(Parcel in) { 711 boolean inclHistory = in.readBoolean(); 712 if (inclHistory) { 713 readFromParcel(in); 714 } 715 716 mHistoryTagPool.clear(); 717 mNextHistoryTagIdx = 0; 718 mNumHistoryTagChars = 0; 719 720 int numTags = in.readInt(); 721 for (int i = 0; i < numTags; i++) { 722 int idx = in.readInt(); 723 String str = in.readString(); 724 int uid = in.readInt(); 725 HistoryTag tag = new HistoryTag(); 726 tag.string = str; 727 tag.uid = uid; 728 tag.poolIdx = idx; 729 mHistoryTagPool.put(tag, idx); 730 if (idx >= mNextHistoryTagIdx) { 731 mNextHistoryTagIdx = idx + 1; 732 } 733 mNumHistoryTagChars += tag.string.length() + 1; 734 } 735 } 736 737 /** 738 * Read all history files and serialize into a big Parcel. 739 * Checkin file calls this method. 740 * 741 * @param out the output parcel 742 */ writeToParcel(Parcel out)743 public void writeToParcel(Parcel out) { 744 writeHistoryBuffer(out); 745 writeToParcel(out, false /* useBlobs */); 746 } 747 748 /** 749 * This is for Settings app, when Settings app receives big history parcel, it call 750 * this method to parse it into list of parcels. 751 * 752 * @param out the output parcel 753 */ writeToBatteryUsageStatsParcel(Parcel out)754 public void writeToBatteryUsageStatsParcel(Parcel out) { 755 out.writeBlob(mHistoryBuffer.marshall()); 756 writeToParcel(out, true /* useBlobs */); 757 } 758 writeToParcel(Parcel out, boolean useBlobs)759 private void writeToParcel(Parcel out, boolean useBlobs) { 760 final long start = SystemClock.uptimeMillis(); 761 out.writeInt(mFileNumbers.size() - 1); 762 for (int i = 0; i < mFileNumbers.size() - 1; i++) { 763 AtomicFile file = getFile(mFileNumbers.get(i)); 764 byte[] raw = new byte[0]; 765 try { 766 raw = file.readFully(); 767 } catch (Exception e) { 768 Slog.e(TAG, "Error reading file " + file.getBaseFile().getPath(), e); 769 } 770 if (useBlobs) { 771 out.writeBlob(raw); 772 } else { 773 // Avoiding blobs in the check-in file for compatibility 774 out.writeByteArray(raw); 775 } 776 } 777 if (DEBUG) { 778 Slog.d(TAG, "writeToParcel duration ms:" + (SystemClock.uptimeMillis() - start)); 779 } 780 } 781 782 /** 783 * Reads a BatteryStatsHistory from a parcel written with 784 * the {@link #writeToBatteryUsageStatsParcel} method. 785 */ createFromBatteryUsageStatsParcel(Parcel in)786 public static BatteryStatsHistory createFromBatteryUsageStatsParcel(Parcel in) { 787 return new BatteryStatsHistory(in); 788 } 789 790 /** 791 * Read history from a check-in file. 792 */ readSummary()793 public boolean readSummary() { 794 if (mActiveFile == null) { 795 Slog.w(TAG, "readSummary: no history file associated with this instance"); 796 return false; 797 } 798 799 Parcel parcel = Parcel.obtain(); 800 try { 801 final long start = SystemClock.uptimeMillis(); 802 if (mActiveFile.exists()) { 803 byte[] raw = mActiveFile.readFully(); 804 if (raw.length > 0) { 805 parcel.unmarshall(raw, 0, raw.length); 806 parcel.setDataPosition(0); 807 readHistoryBuffer(parcel); 808 } 809 if (DEBUG) { 810 Slog.d(TAG, "read history file::" 811 + mActiveFile.getBaseFile().getPath() 812 + " bytes:" + raw.length + " took ms:" + (SystemClock.uptimeMillis() 813 - start)); 814 } 815 } 816 } catch (Exception e) { 817 Slog.e(TAG, "Error reading battery history", e); 818 reset(); 819 return false; 820 } finally { 821 parcel.recycle(); 822 } 823 return true; 824 } 825 826 /** 827 * This is for the check-in file, which has all history files embedded. 828 * 829 * @param in the input parcel. 830 */ readFromParcel(Parcel in)831 public void readFromParcel(Parcel in) { 832 readHistoryBuffer(in); 833 readFromParcel(in, false /* useBlobs */); 834 } 835 readFromParcel(Parcel in, boolean useBlobs)836 private void readFromParcel(Parcel in, boolean useBlobs) { 837 final long start = SystemClock.uptimeMillis(); 838 mHistoryParcels = new ArrayList<>(); 839 final int count = in.readInt(); 840 for (int i = 0; i < count; i++) { 841 byte[] temp = useBlobs ? in.readBlob() : in.createByteArray(); 842 if (temp == null || temp.length == 0) { 843 continue; 844 } 845 Parcel p = Parcel.obtain(); 846 p.unmarshall(temp, 0, temp.length); 847 p.setDataPosition(0); 848 mHistoryParcels.add(p); 849 } 850 if (DEBUG) { 851 Slog.d(TAG, "readFromParcel duration ms:" + (SystemClock.uptimeMillis() - start)); 852 } 853 } 854 855 /** 856 * @return true if there is more than 100MB free disk space left. 857 */ hasFreeDiskSpace()858 private boolean hasFreeDiskSpace() { 859 final StatFs stats = new StatFs(mHistoryDir.getAbsolutePath()); 860 return stats.getAvailableBytes() > MIN_FREE_SPACE; 861 } 862 863 @VisibleForTesting getFilesNumbers()864 public List<Integer> getFilesNumbers() { 865 return mFileNumbers; 866 } 867 868 @VisibleForTesting getActiveFile()869 public AtomicFile getActiveFile() { 870 return mActiveFile; 871 } 872 873 /** 874 * @return the total size of all history files and history buffer. 875 */ getHistoryUsedSize()876 public int getHistoryUsedSize() { 877 int ret = 0; 878 for (int i = 0; i < mFileNumbers.size() - 1; i++) { 879 ret += getFile(mFileNumbers.get(i)).getBaseFile().length(); 880 } 881 ret += mHistoryBuffer.dataSize(); 882 if (mHistoryParcels != null) { 883 for (int i = 0; i < mHistoryParcels.size(); i++) { 884 ret += mHistoryParcels.get(i).dataSize(); 885 } 886 } 887 return ret; 888 } 889 890 /** 891 * Enables/disables recording of history. When disabled, all "record*" calls are a no-op. 892 */ setHistoryRecordingEnabled(boolean enabled)893 public void setHistoryRecordingEnabled(boolean enabled) { 894 mRecordingHistory = enabled; 895 } 896 897 /** 898 * Returns true if history recording is enabled. 899 */ isRecordingHistory()900 public boolean isRecordingHistory() { 901 return mRecordingHistory; 902 } 903 904 /** 905 * Forces history recording regardless of charging state. 906 */ 907 @VisibleForTesting forceRecordAllHistory()908 public void forceRecordAllHistory() { 909 mHaveBatteryLevel = true; 910 mRecordingHistory = true; 911 } 912 913 /** 914 * Starts a history buffer by recording the current wall-clock time. 915 */ startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs, boolean reset)916 public void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs, 917 boolean reset) { 918 mRecordingHistory = true; 919 mHistoryCur.currentTime = mClock.currentTimeMillis(); 920 writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, 921 reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME); 922 mHistoryCur.currentTime = 0; 923 } 924 925 /** 926 * Prepares to continue recording after restoring previous history from persistent storage. 927 */ continueRecordingHistory()928 public void continueRecordingHistory() { 929 if (mHistoryBuffer.dataPosition() <= 0 && mFileNumbers.size() <= 1) { 930 return; 931 } 932 933 mRecordingHistory = true; 934 final long elapsedRealtimeMs = mClock.elapsedRealtime(); 935 final long uptimeMs = mClock.uptimeMillis(); 936 writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_START); 937 startRecordingHistory(elapsedRealtimeMs, uptimeMs, false); 938 } 939 940 /** 941 * Notes the current battery state to be reflected in the next written history item. 942 */ setBatteryState(boolean charging, int status, int level, int chargeUah)943 public void setBatteryState(boolean charging, int status, int level, int chargeUah) { 944 mHaveBatteryLevel = true; 945 setChargingState(charging); 946 mHistoryCur.batteryStatus = (byte) status; 947 mHistoryCur.batteryLevel = (byte) level; 948 mHistoryCur.batteryChargeUah = chargeUah; 949 } 950 951 /** 952 * Notes the current battery state to be reflected in the next written history item. 953 */ setBatteryState(int status, int level, int health, int plugType, int temperature, int voltageMv, int chargeUah)954 public void setBatteryState(int status, int level, int health, int plugType, int temperature, 955 int voltageMv, int chargeUah) { 956 mHaveBatteryLevel = true; 957 mHistoryCur.batteryStatus = (byte) status; 958 mHistoryCur.batteryLevel = (byte) level; 959 mHistoryCur.batteryHealth = (byte) health; 960 mHistoryCur.batteryPlugType = (byte) plugType; 961 mHistoryCur.batteryTemperature = (short) temperature; 962 mHistoryCur.batteryVoltage = (char) voltageMv; 963 mHistoryCur.batteryChargeUah = chargeUah; 964 } 965 966 /** 967 * Notes the current power plugged-in state to be reflected in the next written history item. 968 */ setPluggedInState(boolean pluggedIn)969 public void setPluggedInState(boolean pluggedIn) { 970 if (pluggedIn) { 971 mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG; 972 } else { 973 mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG; 974 } 975 } 976 977 /** 978 * Notes the current battery charging state to be reflected in the next written history item. 979 */ setChargingState(boolean charging)980 public void setChargingState(boolean charging) { 981 if (charging) { 982 mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG; 983 } else { 984 mHistoryCur.states2 &= ~HistoryItem.STATE2_CHARGING_FLAG; 985 } 986 } 987 988 /** 989 * Records a history event with the given code, name and UID. 990 */ recordEvent(long elapsedRealtimeMs, long uptimeMs, int code, String name, int uid)991 public void recordEvent(long elapsedRealtimeMs, long uptimeMs, int code, String name, 992 int uid) { 993 mHistoryCur.eventCode = code; 994 mHistoryCur.eventTag = mHistoryCur.localEventTag; 995 mHistoryCur.eventTag.string = name; 996 mHistoryCur.eventTag.uid = uid; 997 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 998 } 999 1000 /** 1001 * Records a time change event. 1002 */ recordCurrentTimeChange(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs)1003 public void recordCurrentTimeChange(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) { 1004 if (!mRecordingHistory) { 1005 return; 1006 } 1007 1008 mHistoryCur.currentTime = currentTimeMs; 1009 writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, 1010 HistoryItem.CMD_CURRENT_TIME); 1011 mHistoryCur.currentTime = 0; 1012 } 1013 1014 /** 1015 * Records a system shutdown event. 1016 */ recordShutdownEvent(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs)1017 public void recordShutdownEvent(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) { 1018 if (!mRecordingHistory) { 1019 return; 1020 } 1021 1022 mHistoryCur.currentTime = currentTimeMs; 1023 writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_SHUTDOWN); 1024 mHistoryCur.currentTime = 0; 1025 } 1026 1027 /** 1028 * Records a battery state change event. 1029 */ recordBatteryState(long elapsedRealtimeMs, long uptimeMs, int batteryLevel, boolean isPlugged)1030 public void recordBatteryState(long elapsedRealtimeMs, long uptimeMs, int batteryLevel, 1031 boolean isPlugged) { 1032 mHistoryCur.batteryLevel = (byte) batteryLevel; 1033 setPluggedInState(isPlugged); 1034 if (DEBUG) { 1035 Slog.v(TAG, "Battery unplugged to: " 1036 + Integer.toHexString(mHistoryCur.states)); 1037 } 1038 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1039 } 1040 1041 /** 1042 * Records measured energy data. 1043 */ recordEnergyConsumerDetails(long elapsedRealtimeMs, long uptimeMs, EnergyConsumerDetails energyConsumerDetails)1044 public void recordEnergyConsumerDetails(long elapsedRealtimeMs, long uptimeMs, 1045 EnergyConsumerDetails energyConsumerDetails) { 1046 mHistoryCur.energyConsumerDetails = energyConsumerDetails; 1047 mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG; 1048 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1049 } 1050 1051 /** 1052 * Records a history item with the amount of charge consumed by WiFi. Used on certain devices 1053 * equipped with on-device power metering. 1054 */ recordWifiConsumedCharge(long elapsedRealtimeMs, long uptimeMs, double monitoredRailChargeMah)1055 public void recordWifiConsumedCharge(long elapsedRealtimeMs, long uptimeMs, 1056 double monitoredRailChargeMah) { 1057 mHistoryCur.wifiRailChargeMah += monitoredRailChargeMah; 1058 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1059 } 1060 1061 /** 1062 * Records a wakelock start event. 1063 */ recordWakelockStartEvent(long elapsedRealtimeMs, long uptimeMs, String historyName, int uid)1064 public void recordWakelockStartEvent(long elapsedRealtimeMs, long uptimeMs, String historyName, 1065 int uid) { 1066 mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; 1067 mHistoryCur.wakelockTag.string = historyName; 1068 mHistoryCur.wakelockTag.uid = uid; 1069 recordStateStartEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG); 1070 } 1071 1072 /** 1073 * Updates the previous history event with a wakelock name and UID. 1074 */ maybeUpdateWakelockTag(long elapsedRealtimeMs, long uptimeMs, String historyName, int uid)1075 public boolean maybeUpdateWakelockTag(long elapsedRealtimeMs, long uptimeMs, String historyName, 1076 int uid) { 1077 if (mHistoryLastWritten.cmd != HistoryItem.CMD_UPDATE) { 1078 return false; 1079 } 1080 if (mHistoryLastWritten.wakelockTag != null) { 1081 // We'll try to update the last tag. 1082 mHistoryLastWritten.wakelockTag = null; 1083 mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; 1084 mHistoryCur.wakelockTag.string = historyName; 1085 mHistoryCur.wakelockTag.uid = uid; 1086 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1087 } 1088 return true; 1089 } 1090 1091 /** 1092 * Records a wakelock release event. 1093 */ recordWakelockStopEvent(long elapsedRealtimeMs, long uptimeMs, String historyName, int uid)1094 public void recordWakelockStopEvent(long elapsedRealtimeMs, long uptimeMs, String historyName, 1095 int uid) { 1096 mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; 1097 mHistoryCur.wakelockTag.string = historyName != null ? historyName : ""; 1098 mHistoryCur.wakelockTag.uid = uid; 1099 recordStateStopEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG); 1100 } 1101 1102 /** 1103 * Records an event when some state flag changes to true. 1104 */ recordStateStartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags)1105 public void recordStateStartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { 1106 mHistoryCur.states |= stateFlags; 1107 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1108 } 1109 1110 /** 1111 * Records an event when some state flag changes to false. 1112 */ recordStateStopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags)1113 public void recordStateStopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { 1114 mHistoryCur.states &= ~stateFlags; 1115 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1116 } 1117 1118 /** 1119 * Records an event when some state flags change to true and some to false. 1120 */ recordStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int stateStartFlags, int stateStopFlags)1121 public void recordStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int stateStartFlags, 1122 int stateStopFlags) { 1123 mHistoryCur.states = (mHistoryCur.states | stateStartFlags) & ~stateStopFlags; 1124 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1125 } 1126 1127 /** 1128 * Records an event when some state2 flag changes to true. 1129 */ recordState2StartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags)1130 public void recordState2StartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { 1131 mHistoryCur.states2 |= stateFlags; 1132 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1133 } 1134 1135 /** 1136 * Records an event when some state2 flag changes to false. 1137 */ recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags)1138 public void recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { 1139 mHistoryCur.states2 &= ~stateFlags; 1140 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1141 } 1142 1143 /** 1144 * Records an wakeup event. 1145 */ recordWakeupEvent(long elapsedRealtimeMs, long uptimeMs, String reason)1146 public void recordWakeupEvent(long elapsedRealtimeMs, long uptimeMs, String reason) { 1147 mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag; 1148 mHistoryCur.wakeReasonTag.string = reason; 1149 mHistoryCur.wakeReasonTag.uid = 0; 1150 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1151 } 1152 1153 /** 1154 * Records a screen brightness change event. 1155 */ recordScreenBrightnessEvent(long elapsedRealtimeMs, long uptimeMs, int brightnessBin)1156 public void recordScreenBrightnessEvent(long elapsedRealtimeMs, long uptimeMs, 1157 int brightnessBin) { 1158 mHistoryCur.states = setBitField(mHistoryCur.states, brightnessBin, 1159 HistoryItem.STATE_BRIGHTNESS_SHIFT, 1160 HistoryItem.STATE_BRIGHTNESS_MASK); 1161 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1162 } 1163 1164 /** 1165 * Records a GNSS signal level change event. 1166 */ recordGpsSignalQualityEvent(long elapsedRealtimeMs, long uptimeMs, int signalLevel)1167 public void recordGpsSignalQualityEvent(long elapsedRealtimeMs, long uptimeMs, 1168 int signalLevel) { 1169 mHistoryCur.states2 = setBitField(mHistoryCur.states2, signalLevel, 1170 HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, 1171 HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK); 1172 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1173 } 1174 1175 /** 1176 * Records a device idle mode change event. 1177 */ recordDeviceIdleEvent(long elapsedRealtimeMs, long uptimeMs, int mode)1178 public void recordDeviceIdleEvent(long elapsedRealtimeMs, long uptimeMs, int mode) { 1179 mHistoryCur.states2 = setBitField(mHistoryCur.states2, mode, 1180 HistoryItem.STATE2_DEVICE_IDLE_SHIFT, 1181 HistoryItem.STATE2_DEVICE_IDLE_MASK); 1182 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1183 } 1184 1185 /** 1186 * Records a telephony state change event. 1187 */ recordPhoneStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int addStateFlag, int removeStateFlag, int state, int signalStrength)1188 public void recordPhoneStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int addStateFlag, 1189 int removeStateFlag, int state, int signalStrength) { 1190 mHistoryCur.states = (mHistoryCur.states | addStateFlag) & ~removeStateFlag; 1191 if (state != -1) { 1192 mHistoryCur.states = 1193 setBitField(mHistoryCur.states, state, 1194 HistoryItem.STATE_PHONE_STATE_SHIFT, 1195 HistoryItem.STATE_PHONE_STATE_MASK); 1196 } 1197 if (signalStrength != -1) { 1198 mHistoryCur.states = 1199 setBitField(mHistoryCur.states, signalStrength, 1200 HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT, 1201 HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK); 1202 } 1203 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1204 } 1205 1206 /** 1207 * Records a data connection type change event. 1208 */ recordDataConnectionTypeChangeEvent(long elapsedRealtimeMs, long uptimeMs, int dataConnectionType)1209 public void recordDataConnectionTypeChangeEvent(long elapsedRealtimeMs, long uptimeMs, 1210 int dataConnectionType) { 1211 mHistoryCur.states = setBitField(mHistoryCur.states, dataConnectionType, 1212 HistoryItem.STATE_DATA_CONNECTION_SHIFT, 1213 HistoryItem.STATE_DATA_CONNECTION_MASK); 1214 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1215 } 1216 1217 /** 1218 * Records a WiFi supplicant state change event. 1219 */ recordWifiSupplicantStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int supplState)1220 public void recordWifiSupplicantStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, 1221 int supplState) { 1222 mHistoryCur.states2 = 1223 setBitField(mHistoryCur.states2, supplState, 1224 HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT, 1225 HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK); 1226 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1227 } 1228 1229 /** 1230 * Records a WiFi signal strength change event. 1231 */ recordWifiSignalStrengthChangeEvent(long elapsedRealtimeMs, long uptimeMs, int strengthBin)1232 public void recordWifiSignalStrengthChangeEvent(long elapsedRealtimeMs, long uptimeMs, 1233 int strengthBin) { 1234 mHistoryCur.states2 = 1235 setBitField(mHistoryCur.states2, strengthBin, 1236 HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, 1237 HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK); 1238 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1239 } 1240 1241 /** 1242 * Writes event details into Atrace. 1243 */ recordTraceEvents(int code, HistoryTag tag)1244 private void recordTraceEvents(int code, HistoryTag tag) { 1245 if (code == HistoryItem.EVENT_NONE) return; 1246 1247 final int idx = code & HistoryItem.EVENT_TYPE_MASK; 1248 final String prefix = (code & HistoryItem.EVENT_FLAG_START) != 0 ? "+" : 1249 (code & HistoryItem.EVENT_FLAG_FINISH) != 0 ? "-" : ""; 1250 1251 final String[] names = BatteryStats.HISTORY_EVENT_NAMES; 1252 if (idx < 0 || idx >= names.length) return; 1253 1254 final String track = "battery_stats." + names[idx]; 1255 final String name = prefix + names[idx] + "=" + tag.uid + ":\"" + tag.string + "\""; 1256 mTracer.traceInstantEvent(track, name); 1257 } 1258 1259 /** 1260 * Records CPU usage by a specific UID. The recorded data is the delta from 1261 * the previous record for the same UID. 1262 */ recordCpuUsage(long elapsedRealtimeMs, long uptimeMs, CpuUsageDetails cpuUsageDetails)1263 public void recordCpuUsage(long elapsedRealtimeMs, long uptimeMs, 1264 CpuUsageDetails cpuUsageDetails) { 1265 mHistoryCur.cpuUsageDetails = cpuUsageDetails; 1266 mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG; 1267 writeHistoryItem(elapsedRealtimeMs, uptimeMs); 1268 } 1269 1270 /** 1271 * Writes changes to a HistoryItem state bitmap to Atrace. 1272 */ recordTraceCounters(int oldval, int newval, BitDescription[] descriptions)1273 private void recordTraceCounters(int oldval, int newval, BitDescription[] descriptions) { 1274 int diff = oldval ^ newval; 1275 if (diff == 0) return; 1276 1277 for (int i = 0; i < descriptions.length; i++) { 1278 BitDescription bd = descriptions[i]; 1279 if ((diff & bd.mask) == 0) continue; 1280 1281 int value; 1282 if (bd.shift < 0) { 1283 value = (newval & bd.mask) != 0 ? 1 : 0; 1284 } else { 1285 value = (newval & bd.mask) >> bd.shift; 1286 } 1287 1288 mTracer.traceCounter("battery_stats." + bd.name, value); 1289 } 1290 } 1291 setBitField(int bits, int value, int shift, int mask)1292 private int setBitField(int bits, int value, int shift, int mask) { 1293 int shiftedValue = value << shift; 1294 if ((shiftedValue & ~mask) != 0) { 1295 Slog.wtfStack(TAG, "Value " + Integer.toHexString(value) 1296 + " does not fit in the bit field: " + Integer.toHexString(mask)); 1297 shiftedValue &= mask; 1298 } 1299 return (bits & ~mask) | shiftedValue; 1300 } 1301 1302 /** 1303 * Writes the current history item to history. 1304 */ writeHistoryItem(long elapsedRealtimeMs, long uptimeMs)1305 public void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs) { 1306 if (mTrackRunningHistoryElapsedRealtimeMs != 0) { 1307 final long diffElapsedMs = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs; 1308 final long diffUptimeMs = uptimeMs - mTrackRunningHistoryUptimeMs; 1309 if (diffUptimeMs < (diffElapsedMs - 20)) { 1310 final long wakeElapsedTimeMs = elapsedRealtimeMs - (diffElapsedMs - diffUptimeMs); 1311 mHistoryAddTmp.setTo(mHistoryLastWritten); 1312 mHistoryAddTmp.wakelockTag = null; 1313 mHistoryAddTmp.wakeReasonTag = null; 1314 mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE; 1315 mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG; 1316 writeHistoryItem(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp); 1317 } 1318 } 1319 mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG; 1320 mTrackRunningHistoryElapsedRealtimeMs = elapsedRealtimeMs; 1321 mTrackRunningHistoryUptimeMs = uptimeMs; 1322 writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur); 1323 } 1324 writeHistoryItem(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur)1325 private void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) { 1326 if (mTracer != null && mTracer.tracingEnabled()) { 1327 recordTraceEvents(cur.eventCode, cur.eventTag); 1328 recordTraceCounters(mTraceLastState, cur.states, 1329 BatteryStats.HISTORY_STATE_DESCRIPTIONS); 1330 recordTraceCounters(mTraceLastState2, cur.states2, 1331 BatteryStats.HISTORY_STATE2_DESCRIPTIONS); 1332 mTraceLastState = cur.states; 1333 mTraceLastState2 = cur.states2; 1334 } 1335 1336 if (!mHaveBatteryLevel || !mRecordingHistory) { 1337 return; 1338 } 1339 1340 if (!mMutable) { 1341 throw new ConcurrentModificationException("Battery history is not writable"); 1342 } 1343 1344 final long timeDiffMs = (mHistoryBaseTimeMs + elapsedRealtimeMs) - mHistoryLastWritten.time; 1345 final int diffStates = mHistoryLastWritten.states ^ cur.states; 1346 final int diffStates2 = mHistoryLastWritten.states2 ^ cur.states2; 1347 final int lastDiffStates = mHistoryLastWritten.states ^ mHistoryLastLastWritten.states; 1348 final int lastDiffStates2 = mHistoryLastWritten.states2 ^ mHistoryLastLastWritten.states2; 1349 if (DEBUG) { 1350 Slog.i(TAG, "ADD: tdelta=" + timeDiffMs + " diff=" 1351 + Integer.toHexString(diffStates) + " lastDiff=" 1352 + Integer.toHexString(lastDiffStates) + " diff2=" 1353 + Integer.toHexString(diffStates2) + " lastDiff2=" 1354 + Integer.toHexString(lastDiffStates2)); 1355 } 1356 1357 if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE 1358 && timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0 1359 && (diffStates2 & lastDiffStates2) == 0 1360 && (!mHistoryLastWritten.tagsFirstOccurrence && !cur.tagsFirstOccurrence) 1361 && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null) 1362 && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null) 1363 && mHistoryLastWritten.stepDetails == null 1364 && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE 1365 || cur.eventCode == HistoryItem.EVENT_NONE) 1366 && mHistoryLastWritten.batteryLevel == cur.batteryLevel 1367 && mHistoryLastWritten.batteryStatus == cur.batteryStatus 1368 && mHistoryLastWritten.batteryHealth == cur.batteryHealth 1369 && mHistoryLastWritten.batteryPlugType == cur.batteryPlugType 1370 && mHistoryLastWritten.batteryTemperature == cur.batteryTemperature 1371 && mHistoryLastWritten.batteryVoltage == cur.batteryVoltage 1372 && mHistoryLastWritten.energyConsumerDetails == null 1373 && mHistoryLastWritten.cpuUsageDetails == null) { 1374 // We can merge this new change in with the last one. Merging is 1375 // allowed as long as only the states have changed, and within those states 1376 // as long as no bit has changed both between now and the last entry, as 1377 // well as the last entry and the one before it (so we capture any toggles). 1378 if (DEBUG) Slog.i(TAG, "ADD: rewinding back to " + mHistoryBufferLastPos); 1379 mHistoryBuffer.setDataSize(mHistoryBufferLastPos); 1380 mHistoryBuffer.setDataPosition(mHistoryBufferLastPos); 1381 mHistoryBufferLastPos = -1; 1382 elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTimeMs; 1383 // If the last written history had a wakelock tag, we need to retain it. 1384 // Note that the condition above made sure that we aren't in a case where 1385 // both it and the current history item have a wakelock tag. 1386 if (mHistoryLastWritten.wakelockTag != null) { 1387 cur.wakelockTag = cur.localWakelockTag; 1388 cur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag); 1389 } 1390 // If the last written history had a wake reason tag, we need to retain it. 1391 // Note that the condition above made sure that we aren't in a case where 1392 // both it and the current history item have a wakelock tag. 1393 if (mHistoryLastWritten.wakeReasonTag != null) { 1394 cur.wakeReasonTag = cur.localWakeReasonTag; 1395 cur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag); 1396 } 1397 // If the last written history had an event, we need to retain it. 1398 // Note that the condition above made sure that we aren't in a case where 1399 // both it and the current history item have an event. 1400 if (mHistoryLastWritten.eventCode != HistoryItem.EVENT_NONE) { 1401 cur.eventCode = mHistoryLastWritten.eventCode; 1402 cur.eventTag = cur.localEventTag; 1403 cur.eventTag.setTo(mHistoryLastWritten.eventTag); 1404 } 1405 mHistoryLastWritten.setTo(mHistoryLastLastWritten); 1406 } 1407 final int dataSize = mHistoryBuffer.dataSize(); 1408 1409 if (dataSize >= mMaxHistoryBufferSize) { 1410 if (mMaxHistoryBufferSize == 0) { 1411 Slog.wtf(TAG, "mMaxHistoryBufferSize should not be zero when writing history"); 1412 mMaxHistoryBufferSize = 1024; 1413 } 1414 1415 //open a new history file. 1416 final long start = SystemClock.uptimeMillis(); 1417 writeHistory(); 1418 if (DEBUG) { 1419 Slog.d(TAG, "addHistoryBufferLocked writeHistory took ms:" 1420 + (SystemClock.uptimeMillis() - start)); 1421 } 1422 startNextFile(); 1423 mHistoryBuffer.setDataSize(0); 1424 mHistoryBuffer.setDataPosition(0); 1425 mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2); 1426 mHistoryBufferLastPos = -1; 1427 mHistoryLastWritten.clear(); 1428 mHistoryLastLastWritten.clear(); 1429 1430 // Mark every entry in the pool with a flag indicating that the tag 1431 // has not yet been encountered while writing the current history buffer. 1432 for (Map.Entry<HistoryTag, Integer> entry : mHistoryTagPool.entrySet()) { 1433 entry.setValue(entry.getValue() | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG); 1434 } 1435 mMeasuredEnergyHeaderWritten = false; 1436 mCpuUsageHeaderWritten = false; 1437 1438 // Make a copy of mHistoryCur. 1439 HistoryItem copy = new HistoryItem(); 1440 copy.setTo(cur); 1441 // startRecordingHistory will reset mHistoryCur. 1442 startRecordingHistory(elapsedRealtimeMs, uptimeMs, false); 1443 // Add the copy into history buffer. 1444 writeHistoryItem(elapsedRealtimeMs, uptimeMs, copy, HistoryItem.CMD_UPDATE); 1445 return; 1446 } 1447 1448 if (dataSize == 0) { 1449 // The history is currently empty; we need it to start with a time stamp. 1450 HistoryItem copy = new HistoryItem(); 1451 copy.setTo(cur); 1452 copy.currentTime = mClock.currentTimeMillis(); 1453 copy.wakelockTag = null; 1454 copy.wakeReasonTag = null; 1455 copy.eventCode = HistoryItem.EVENT_NONE; 1456 copy.eventTag = null; 1457 copy.tagsFirstOccurrence = false; 1458 copy.energyConsumerDetails = null; 1459 copy.cpuUsageDetails = null; 1460 writeHistoryItem(elapsedRealtimeMs, uptimeMs, copy, HistoryItem.CMD_RESET); 1461 } 1462 writeHistoryItem(elapsedRealtimeMs, uptimeMs, cur, HistoryItem.CMD_UPDATE); 1463 } 1464 writeHistoryItem(long elapsedRealtimeMs, @SuppressWarnings(R) long uptimeMs, HistoryItem cur, byte cmd)1465 private void writeHistoryItem(long elapsedRealtimeMs, 1466 @SuppressWarnings("UnusedVariable") long uptimeMs, HistoryItem cur, byte cmd) { 1467 if (!mMutable) { 1468 throw new ConcurrentModificationException("Battery history is not writable"); 1469 } 1470 mHistoryBufferLastPos = mHistoryBuffer.dataPosition(); 1471 mHistoryLastLastWritten.setTo(mHistoryLastWritten); 1472 final boolean hasTags = mHistoryLastWritten.tagsFirstOccurrence || cur.tagsFirstOccurrence; 1473 mHistoryLastWritten.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur); 1474 if (mHistoryLastWritten.time < mHistoryLastLastWritten.time - 60000) { 1475 Slog.wtf(TAG, "Significantly earlier event written to battery history:" 1476 + " time=" + mHistoryLastWritten.time 1477 + " previous=" + mHistoryLastLastWritten.time); 1478 } 1479 mHistoryLastWritten.tagsFirstOccurrence = hasTags; 1480 writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten); 1481 mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs; 1482 cur.wakelockTag = null; 1483 cur.wakeReasonTag = null; 1484 cur.eventCode = HistoryItem.EVENT_NONE; 1485 cur.eventTag = null; 1486 cur.tagsFirstOccurrence = false; 1487 cur.energyConsumerDetails = null; 1488 cur.cpuUsageDetails = null; 1489 if (DEBUG) { 1490 Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos 1491 + " now " + mHistoryBuffer.dataPosition() 1492 + " size is now " + mHistoryBuffer.dataSize()); 1493 } 1494 } 1495 1496 /* 1497 The history delta format uses flags to denote further data in subsequent ints in the parcel. 1498 1499 There is always the first token, which may contain the delta time, or an indicator of 1500 the length of the time (int or long) following this token. 1501 1502 First token: always present, 1503 31 23 15 7 0 1504 █M|L|K|J|I|H|G|F█E|D|C|B|A|T|T|T█T|T|T|T|T|T|T|T█T|T|T|T|T|T|T|T█ 1505 1506 T: the delta time if it is <= 0x7fffd. Otherwise 0x7fffe indicates an int immediately 1507 follows containing the time, and 0x7ffff indicates a long immediately follows with the 1508 delta time. 1509 A: battery level changed and an int follows with battery data. 1510 B: state changed and an int follows with state change data. 1511 C: state2 has changed and an int follows with state2 change data. 1512 D: wakelock/wakereason has changed and an wakelock/wakereason struct follows. 1513 E: event data has changed and an event struct follows. 1514 F: battery charge in coulombs has changed and an int with the charge follows. 1515 G: state flag denoting that the mobile radio was active. 1516 H: state flag denoting that the wifi radio was active. 1517 I: state flag denoting that a wifi scan occurred. 1518 J: state flag denoting that a wifi full lock was held. 1519 K: state flag denoting that the gps was on. 1520 L: state flag denoting that a wakelock was held. 1521 M: state flag denoting that the cpu was running. 1522 1523 Time int/long: if T in the first token is 0x7ffff or 0x7fffe, then an int or long follows 1524 with the time delta. 1525 1526 Battery level int: if A in the first token is set, 1527 31 23 15 7 0 1528 █L|L|L|L|L|L|L|T█T|T|T|T|T|T|T|T█T|V|V|V|V|V|V|V█V|V|V|V|V|V|V|D█ 1529 1530 D: indicates that extra history details follow. 1531 V: the battery voltage. 1532 T: the battery temperature. 1533 L: the battery level (out of 100). 1534 1535 State change int: if B in the first token is set, 1536 31 23 15 7 0 1537 █S|S|S|H|H|H|P|P█F|E|D|C|B| | |A█ | | | | | | | █ | | | | | | | █ 1538 1539 A: wifi multicast was on. 1540 B: battery was plugged in. 1541 C: screen was on. 1542 D: phone was scanning for signal. 1543 E: audio was on. 1544 F: a sensor was active. 1545 1546 State2 change int: if C in the first token is set, 1547 31 23 15 7 0 1548 █M|L|K|J|I|H|H|G█F|E|D|C| | | | █ | | | | | | | █ |B|B|B|A|A|A|A█ 1549 1550 A: 4 bits indicating the wifi supplicant state: {@link BatteryStats#WIFI_SUPPL_STATE_NAMES}. 1551 B: 3 bits indicating the wifi signal strength: 0, 1, 2, 3, 4. 1552 C: a bluetooth scan was active. 1553 D: the camera was active. 1554 E: bluetooth was on. 1555 F: a phone call was active. 1556 G: the device was charging. 1557 H: 2 bits indicating the device-idle (doze) state: off, light, full 1558 I: the flashlight was on. 1559 J: wifi was on. 1560 K: wifi was running. 1561 L: video was playing. 1562 M: power save mode was on. 1563 1564 Wakelock/wakereason struct: if D in the first token is set, 1565 Event struct: if E in the first token is set, 1566 History step details struct: if D in the battery level int is set, 1567 1568 Battery charge int: if F in the first token is set, an int representing the battery charge 1569 in coulombs follows. 1570 */ 1571 /** 1572 * Writes the delta between the previous and current history items into history buffer. 1573 */ writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last)1574 public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) { 1575 if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) { 1576 dest.writeInt(BatteryStatsHistory.DELTA_TIME_ABS); 1577 cur.writeToParcel(dest, 0); 1578 return; 1579 } 1580 1581 int extensionFlags = 0; 1582 final long deltaTime = cur.time - last.time; 1583 final int lastBatteryLevelInt = buildBatteryLevelInt(last); 1584 final int lastStateInt = buildStateInt(last); 1585 1586 int deltaTimeToken; 1587 if (deltaTime < 0 || deltaTime > Integer.MAX_VALUE) { 1588 deltaTimeToken = BatteryStatsHistory.DELTA_TIME_LONG; 1589 } else if (deltaTime >= BatteryStatsHistory.DELTA_TIME_ABS) { 1590 deltaTimeToken = BatteryStatsHistory.DELTA_TIME_INT; 1591 } else { 1592 deltaTimeToken = (int) deltaTime; 1593 } 1594 int firstToken = deltaTimeToken | (cur.states & BatteryStatsHistory.DELTA_STATE_MASK); 1595 int batteryLevelInt = buildBatteryLevelInt(cur); 1596 1597 if (cur.batteryLevel < mLastHistoryStepLevel || mLastHistoryStepLevel == 0) { 1598 cur.stepDetails = mStepDetailsCalculator.getHistoryStepDetails(); 1599 if (cur.stepDetails != null) { 1600 batteryLevelInt |= BatteryStatsHistory.BATTERY_LEVEL_DETAILS_FLAG; 1601 mLastHistoryStepLevel = cur.batteryLevel; 1602 } 1603 } else { 1604 cur.stepDetails = null; 1605 mLastHistoryStepLevel = cur.batteryLevel; 1606 } 1607 1608 final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt; 1609 if (batteryLevelIntChanged) { 1610 firstToken |= BatteryStatsHistory.DELTA_BATTERY_LEVEL_FLAG; 1611 } 1612 final int stateInt = buildStateInt(cur); 1613 final boolean stateIntChanged = stateInt != lastStateInt; 1614 if (stateIntChanged) { 1615 firstToken |= BatteryStatsHistory.DELTA_STATE_FLAG; 1616 } 1617 if (cur.energyConsumerDetails != null) { 1618 extensionFlags |= BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_FLAG; 1619 if (!mMeasuredEnergyHeaderWritten) { 1620 extensionFlags |= BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_HEADER_FLAG; 1621 } 1622 } 1623 if (cur.cpuUsageDetails != null) { 1624 extensionFlags |= EXTENSION_CPU_USAGE_FLAG; 1625 if (!mCpuUsageHeaderWritten) { 1626 extensionFlags |= BatteryStatsHistory.EXTENSION_CPU_USAGE_HEADER_FLAG; 1627 } 1628 } 1629 if (extensionFlags != 0) { 1630 cur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG; 1631 } else { 1632 cur.states2 &= ~HistoryItem.STATE2_EXTENSIONS_FLAG; 1633 } 1634 final boolean state2IntChanged = cur.states2 != last.states2 || extensionFlags != 0; 1635 if (state2IntChanged) { 1636 firstToken |= BatteryStatsHistory.DELTA_STATE2_FLAG; 1637 } 1638 if (cur.wakelockTag != null || cur.wakeReasonTag != null) { 1639 firstToken |= BatteryStatsHistory.DELTA_WAKELOCK_FLAG; 1640 } 1641 if (cur.eventCode != HistoryItem.EVENT_NONE) { 1642 firstToken |= BatteryStatsHistory.DELTA_EVENT_FLAG; 1643 } 1644 1645 final boolean batteryChargeChanged = cur.batteryChargeUah != last.batteryChargeUah; 1646 if (batteryChargeChanged) { 1647 firstToken |= BatteryStatsHistory.DELTA_BATTERY_CHARGE_FLAG; 1648 } 1649 dest.writeInt(firstToken); 1650 if (DEBUG) { 1651 Slog.i(TAG, "WRITE DELTA: firstToken=0x" + Integer.toHexString(firstToken) 1652 + " deltaTime=" + deltaTime); 1653 } 1654 1655 if (deltaTimeToken >= BatteryStatsHistory.DELTA_TIME_INT) { 1656 if (deltaTimeToken == BatteryStatsHistory.DELTA_TIME_INT) { 1657 if (DEBUG) Slog.i(TAG, "WRITE DELTA: int deltaTime=" + (int) deltaTime); 1658 dest.writeInt((int) deltaTime); 1659 } else { 1660 if (DEBUG) Slog.i(TAG, "WRITE DELTA: long deltaTime=" + deltaTime); 1661 dest.writeLong(deltaTime); 1662 } 1663 } 1664 if (batteryLevelIntChanged) { 1665 dest.writeInt(batteryLevelInt); 1666 if (DEBUG) { 1667 Slog.i(TAG, "WRITE DELTA: batteryToken=0x" 1668 + Integer.toHexString(batteryLevelInt) 1669 + " batteryLevel=" + cur.batteryLevel 1670 + " batteryTemp=" + cur.batteryTemperature 1671 + " batteryVolt=" + (int) cur.batteryVoltage); 1672 } 1673 } 1674 if (stateIntChanged) { 1675 dest.writeInt(stateInt); 1676 if (DEBUG) { 1677 Slog.i(TAG, "WRITE DELTA: stateToken=0x" 1678 + Integer.toHexString(stateInt) 1679 + " batteryStatus=" + cur.batteryStatus 1680 + " batteryHealth=" + cur.batteryHealth 1681 + " batteryPlugType=" + cur.batteryPlugType 1682 + " states=0x" + Integer.toHexString(cur.states)); 1683 } 1684 } 1685 if (state2IntChanged) { 1686 dest.writeInt(cur.states2); 1687 if (DEBUG) { 1688 Slog.i(TAG, "WRITE DELTA: states2=0x" 1689 + Integer.toHexString(cur.states2)); 1690 } 1691 } 1692 if (cur.wakelockTag != null || cur.wakeReasonTag != null) { 1693 int wakeLockIndex; 1694 int wakeReasonIndex; 1695 if (cur.wakelockTag != null) { 1696 wakeLockIndex = writeHistoryTag(cur.wakelockTag); 1697 if (DEBUG) { 1698 Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx 1699 + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string); 1700 } 1701 } else { 1702 wakeLockIndex = 0xffff; 1703 } 1704 if (cur.wakeReasonTag != null) { 1705 wakeReasonIndex = writeHistoryTag(cur.wakeReasonTag); 1706 if (DEBUG) { 1707 Slog.i(TAG, "WRITE DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx 1708 + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string); 1709 } 1710 } else { 1711 wakeReasonIndex = 0xffff; 1712 } 1713 dest.writeInt((wakeReasonIndex << 16) | wakeLockIndex); 1714 if (cur.wakelockTag != null 1715 && (wakeLockIndex & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) { 1716 cur.wakelockTag.writeToParcel(dest, 0); 1717 cur.tagsFirstOccurrence = true; 1718 } 1719 if (cur.wakeReasonTag != null 1720 && (wakeReasonIndex & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) { 1721 cur.wakeReasonTag.writeToParcel(dest, 0); 1722 cur.tagsFirstOccurrence = true; 1723 } 1724 } 1725 if (cur.eventCode != HistoryItem.EVENT_NONE) { 1726 final int index = writeHistoryTag(cur.eventTag); 1727 final int codeAndIndex = setBitField(cur.eventCode & 0xffff, index, 16, 0xFFFF0000); 1728 dest.writeInt(codeAndIndex); 1729 if ((index & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) { 1730 cur.eventTag.writeToParcel(dest, 0); 1731 cur.tagsFirstOccurrence = true; 1732 } 1733 if (DEBUG) { 1734 Slog.i(TAG, "WRITE DELTA: event=" + cur.eventCode + " tag=#" 1735 + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":" 1736 + cur.eventTag.string); 1737 } 1738 } 1739 1740 if (cur.stepDetails != null) { 1741 cur.stepDetails.writeToParcel(dest); 1742 } 1743 1744 if (batteryChargeChanged) { 1745 if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUah=" + cur.batteryChargeUah); 1746 dest.writeInt(cur.batteryChargeUah); 1747 } 1748 dest.writeDouble(cur.modemRailChargeMah); 1749 dest.writeDouble(cur.wifiRailChargeMah); 1750 if (extensionFlags != 0) { 1751 dest.writeInt(extensionFlags); 1752 if (cur.energyConsumerDetails != null) { 1753 if (DEBUG) { 1754 Slog.i(TAG, "WRITE DELTA: measuredEnergyDetails=" + cur.energyConsumerDetails); 1755 } 1756 if (!mMeasuredEnergyHeaderWritten) { 1757 EnergyConsumerDetails.EnergyConsumer[] consumers = 1758 cur.energyConsumerDetails.consumers; 1759 dest.writeInt(consumers.length); 1760 for (EnergyConsumerDetails.EnergyConsumer consumer : consumers) { 1761 dest.writeInt(consumer.type); 1762 dest.writeInt(consumer.ordinal); 1763 dest.writeString(consumer.name); 1764 } 1765 mMeasuredEnergyHeaderWritten = true; 1766 } 1767 mVarintParceler.writeLongArray(dest, cur.energyConsumerDetails.chargeUC); 1768 } 1769 1770 if (cur.cpuUsageDetails != null) { 1771 if (DEBUG) { 1772 Slog.i(TAG, "WRITE DELTA: cpuUsageDetails=" + cur.cpuUsageDetails); 1773 } 1774 if (!mCpuUsageHeaderWritten) { 1775 dest.writeInt(cur.cpuUsageDetails.cpuBracketDescriptions.length); 1776 for (String desc: cur.cpuUsageDetails.cpuBracketDescriptions) { 1777 dest.writeString(desc); 1778 } 1779 mCpuUsageHeaderWritten = true; 1780 } 1781 dest.writeInt(cur.cpuUsageDetails.uid); 1782 mVarintParceler.writeLongArray(dest, cur.cpuUsageDetails.cpuUsageMs); 1783 } 1784 } 1785 } 1786 buildBatteryLevelInt(HistoryItem h)1787 private int buildBatteryLevelInt(HistoryItem h) { 1788 int bits = 0; 1789 bits = setBitField(bits, h.batteryLevel, 25, 0xfe000000 /* 7F << 25 */); 1790 bits = setBitField(bits, h.batteryTemperature, 15, 0x01ff8000 /* 3FF << 15 */); 1791 bits = setBitField(bits, h.batteryVoltage, 1, 0x00007ffe /* 3FFF << 1 */); 1792 return bits; 1793 } 1794 buildStateInt(HistoryItem h)1795 private int buildStateInt(HistoryItem h) { 1796 int plugType = 0; 1797 if ((h.batteryPlugType & BatteryManager.BATTERY_PLUGGED_AC) != 0) { 1798 plugType = 1; 1799 } else if ((h.batteryPlugType & BatteryManager.BATTERY_PLUGGED_USB) != 0) { 1800 plugType = 2; 1801 } else if ((h.batteryPlugType & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0) { 1802 plugType = 3; 1803 } 1804 return ((h.batteryStatus & BatteryStatsHistory.STATE_BATTERY_STATUS_MASK) 1805 << BatteryStatsHistory.STATE_BATTERY_STATUS_SHIFT) 1806 | ((h.batteryHealth & BatteryStatsHistory.STATE_BATTERY_HEALTH_MASK) 1807 << BatteryStatsHistory.STATE_BATTERY_HEALTH_SHIFT) 1808 | ((plugType & BatteryStatsHistory.STATE_BATTERY_PLUG_MASK) 1809 << BatteryStatsHistory.STATE_BATTERY_PLUG_SHIFT) 1810 | (h.states & (~BatteryStatsHistory.STATE_BATTERY_MASK)); 1811 } 1812 1813 /** 1814 * Returns the index for the specified tag. If this is the first time the tag is encountered 1815 * while writing the current history buffer, the method returns 1816 * <code>(index | TAG_FIRST_OCCURRENCE_FLAG)</code> 1817 */ writeHistoryTag(HistoryTag tag)1818 private int writeHistoryTag(HistoryTag tag) { 1819 if (tag.string == null) { 1820 Slog.wtfStack(TAG, "writeHistoryTag called with null name"); 1821 } 1822 1823 final int stringLength = tag.string.length(); 1824 if (stringLength > MAX_HISTORY_TAG_STRING_LENGTH) { 1825 Slog.e(TAG, "Long battery history tag: " + tag.string); 1826 tag.string = tag.string.substring(0, MAX_HISTORY_TAG_STRING_LENGTH); 1827 } 1828 1829 Integer idxObj = mHistoryTagPool.get(tag); 1830 int idx; 1831 if (idxObj != null) { 1832 idx = idxObj; 1833 if ((idx & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) { 1834 mHistoryTagPool.put(tag, idx & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG); 1835 } 1836 return idx; 1837 } else if (mNextHistoryTagIdx < HISTORY_TAG_INDEX_LIMIT) { 1838 idx = mNextHistoryTagIdx; 1839 HistoryTag key = new HistoryTag(); 1840 key.setTo(tag); 1841 tag.poolIdx = idx; 1842 mHistoryTagPool.put(key, idx); 1843 mNextHistoryTagIdx++; 1844 1845 mNumHistoryTagChars += stringLength + 1; 1846 if (mHistoryTags != null) { 1847 mHistoryTags.put(idx, key); 1848 } 1849 return idx | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG; 1850 } else { 1851 tag.poolIdx = HistoryTag.HISTORY_TAG_POOL_OVERFLOW; 1852 // Tag pool overflow: include the tag itself in the parcel 1853 return HISTORY_TAG_INDEX_LIMIT | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG; 1854 } 1855 } 1856 1857 /** 1858 * Don't allow any more batching in to the current history event. 1859 */ commitCurrentHistoryBatchLocked()1860 public void commitCurrentHistoryBatchLocked() { 1861 mHistoryLastWritten.cmd = HistoryItem.CMD_NULL; 1862 } 1863 1864 /** 1865 * Saves the accumulated history buffer in the active file, see {@link #getActiveFile()} . 1866 */ writeHistory()1867 public void writeHistory() { 1868 if (isReadOnly()) { 1869 Slog.w(TAG, "writeHistory: this instance instance is read-only"); 1870 return; 1871 } 1872 1873 Parcel p = Parcel.obtain(); 1874 try { 1875 final long start = SystemClock.uptimeMillis(); 1876 writeHistoryBuffer(p); 1877 if (DEBUG) { 1878 Slog.d(TAG, "writeHistoryBuffer duration ms:" 1879 + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize()); 1880 } 1881 writeParcelToFileLocked(p, mActiveFile); 1882 } finally { 1883 p.recycle(); 1884 } 1885 } 1886 1887 /** 1888 * Reads history buffer from a persisted Parcel. 1889 */ readHistoryBuffer(Parcel in)1890 public void readHistoryBuffer(Parcel in) throws ParcelFormatException { 1891 final int version = in.readInt(); 1892 if (version != BatteryStatsHistory.VERSION) { 1893 Slog.w("BatteryStats", "readHistoryBuffer: version got " + version 1894 + ", expected " + BatteryStatsHistory.VERSION + "; erasing old stats"); 1895 return; 1896 } 1897 1898 final long historyBaseTime = in.readLong(); 1899 1900 mHistoryBuffer.setDataSize(0); 1901 mHistoryBuffer.setDataPosition(0); 1902 1903 int bufSize = in.readInt(); 1904 int curPos = in.dataPosition(); 1905 if (bufSize >= (mMaxHistoryBufferSize * 100)) { 1906 throw new ParcelFormatException( 1907 "File corrupt: history data buffer too large " + bufSize); 1908 } else if ((bufSize & ~3) != bufSize) { 1909 throw new ParcelFormatException( 1910 "File corrupt: history data buffer not aligned " + bufSize); 1911 } else { 1912 if (DEBUG) { 1913 Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize 1914 + " bytes at " + curPos); 1915 } 1916 mHistoryBuffer.appendFrom(in, curPos, bufSize); 1917 in.setDataPosition(curPos + bufSize); 1918 } 1919 1920 mHistoryBaseTimeMs = historyBaseTime; 1921 if (DEBUG) { 1922 StringBuilder sb = new StringBuilder(128); 1923 sb.append("****************** NEW mHistoryBaseTimeMs: "); 1924 TimeUtils.formatDuration(mHistoryBaseTimeMs, sb); 1925 Slog.i(TAG, sb.toString()); 1926 } 1927 1928 if (mHistoryBaseTimeMs > 0) { 1929 long elapsedRealtimeMs = mClock.elapsedRealtime(); 1930 mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs; 1931 mHistoryBaseTimeMs = mHistoryBaseTimeMs - elapsedRealtimeMs + 1; 1932 if (DEBUG) { 1933 StringBuilder sb = new StringBuilder(128); 1934 sb.append("****************** ADJUSTED mHistoryBaseTimeMs: "); 1935 TimeUtils.formatDuration(mHistoryBaseTimeMs, sb); 1936 Slog.i(TAG, sb.toString()); 1937 } 1938 } 1939 } 1940 writeHistoryBuffer(Parcel out)1941 private void writeHistoryBuffer(Parcel out) { 1942 if (DEBUG) { 1943 StringBuilder sb = new StringBuilder(128); 1944 sb.append("****************** WRITING mHistoryBaseTimeMs: "); 1945 TimeUtils.formatDuration(mHistoryBaseTimeMs, sb); 1946 sb.append(" mLastHistoryElapsedRealtimeMs: "); 1947 TimeUtils.formatDuration(mLastHistoryElapsedRealtimeMs, sb); 1948 Slog.i(TAG, sb.toString()); 1949 } 1950 out.writeInt(BatteryStatsHistory.VERSION); 1951 out.writeLong(mHistoryBaseTimeMs + mLastHistoryElapsedRealtimeMs); 1952 out.writeInt(mHistoryBuffer.dataSize()); 1953 if (DEBUG) { 1954 Slog.i(TAG, "***************** WRITING HISTORY: " 1955 + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition()); 1956 } 1957 out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize()); 1958 } 1959 writeParcelToFileLocked(Parcel p, AtomicFile file)1960 private void writeParcelToFileLocked(Parcel p, AtomicFile file) { 1961 FileOutputStream fos = null; 1962 mWriteLock.lock(); 1963 try { 1964 final long startTimeMs = SystemClock.uptimeMillis(); 1965 fos = file.startWrite(); 1966 fos.write(p.marshall()); 1967 fos.flush(); 1968 file.finishWrite(fos); 1969 if (DEBUG) { 1970 Slog.d(TAG, "writeParcelToFileLocked file:" + file.getBaseFile().getPath() 1971 + " duration ms:" + (SystemClock.uptimeMillis() - startTimeMs) 1972 + " bytes:" + p.dataSize()); 1973 } 1974 com.android.internal.logging.EventLogTags.writeCommitSysConfigFile( 1975 "batterystats", SystemClock.uptimeMillis() - startTimeMs); 1976 } catch (IOException e) { 1977 Slog.w(TAG, "Error writing battery statistics", e); 1978 file.failWrite(fos); 1979 } finally { 1980 mWriteLock.unlock(); 1981 } 1982 } 1983 1984 /** 1985 * Returns the total number of history tags in the tag pool. 1986 */ getHistoryStringPoolSize()1987 public int getHistoryStringPoolSize() { 1988 return mHistoryTagPool.size(); 1989 } 1990 1991 /** 1992 * Returns the total number of bytes occupied by the history tag pool. 1993 */ getHistoryStringPoolBytes()1994 public int getHistoryStringPoolBytes() { 1995 return mNumHistoryTagChars; 1996 } 1997 1998 /** 1999 * Returns the string held by the requested history tag. 2000 */ getHistoryTagPoolString(int index)2001 public String getHistoryTagPoolString(int index) { 2002 ensureHistoryTagArray(); 2003 HistoryTag historyTag = mHistoryTags.get(index); 2004 return historyTag != null ? historyTag.string : null; 2005 } 2006 2007 /** 2008 * Returns the UID held by the requested history tag. 2009 */ getHistoryTagPoolUid(int index)2010 public int getHistoryTagPoolUid(int index) { 2011 ensureHistoryTagArray(); 2012 HistoryTag historyTag = mHistoryTags.get(index); 2013 return historyTag != null ? historyTag.uid : Process.INVALID_UID; 2014 } 2015 ensureHistoryTagArray()2016 private void ensureHistoryTagArray() { 2017 if (mHistoryTags != null) { 2018 return; 2019 } 2020 2021 mHistoryTags = new SparseArray<>(mHistoryTagPool.size()); 2022 for (Map.Entry<HistoryTag, Integer> entry : mHistoryTagPool.entrySet()) { 2023 mHistoryTags.put(entry.getValue() & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG, 2024 entry.getKey()); 2025 } 2026 } 2027 2028 /** 2029 * Writes/reads an array of longs into Parcel using a compact format, where small integers use 2030 * fewer bytes. It is a bit more expensive than just writing the long into the parcel, 2031 * but at scale saves a lot of storage and allows recording of longer battery history. 2032 */ 2033 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 2034 public static final class VarintParceler { 2035 /** 2036 * Writes an array of longs into Parcel using the varint format, see 2037 * https://developers.google.com/protocol-buffers/docs/encoding#varints 2038 */ writeLongArray(Parcel parcel, long[] values)2039 public void writeLongArray(Parcel parcel, long[] values) { 2040 int out = 0; 2041 int shift = 0; 2042 for (long value : values) { 2043 boolean done = false; 2044 while (!done) { 2045 final byte b; 2046 if ((value & ~0x7FL) == 0) { 2047 b = (byte) value; 2048 done = true; 2049 } else { 2050 b = (byte) (((int) value & 0x7F) | 0x80); 2051 value >>>= 7; 2052 } 2053 if (shift == 32) { 2054 parcel.writeInt(out); 2055 shift = 0; 2056 out = 0; 2057 } 2058 out |= (b & 0xFF) << shift; 2059 shift += 8; 2060 } 2061 } 2062 if (shift != 0) { 2063 parcel.writeInt(out); 2064 } 2065 } 2066 2067 /** 2068 * Reads a long written with {@link #writeLongArray} 2069 */ readLongArray(Parcel parcel, long[] values)2070 public void readLongArray(Parcel parcel, long[] values) { 2071 int in = parcel.readInt(); 2072 int available = 4; 2073 for (int i = 0; i < values.length; i++) { 2074 long result = 0; 2075 int shift; 2076 for (shift = 0; shift < 64; shift += 7) { 2077 if (available == 0) { 2078 in = parcel.readInt(); 2079 available = 4; 2080 } 2081 final byte b = (byte) in; 2082 in >>= 8; 2083 available--; 2084 2085 result |= (long) (b & 0x7F) << shift; 2086 if ((b & 0x80) == 0) { 2087 values[i] = result; 2088 break; 2089 } 2090 } 2091 if (shift >= 64) { 2092 throw new ParcelFormatException("Invalid varint format"); 2093 } 2094 } 2095 } 2096 } 2097 } 2098