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