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