• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 android.os;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.database.Cursor;
22 import android.database.CursorWindow;
23 import android.util.Range;
24 import android.util.SparseArray;
25 import android.util.proto.ProtoOutputStream;
26 
27 import com.android.internal.annotations.GuardedBy;
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.internal.os.BatteryStatsHistory;
30 import com.android.internal.os.BatteryStatsHistoryIterator;
31 import com.android.internal.os.MonotonicClock;
32 import com.android.modules.utils.TypedXmlPullParser;
33 import com.android.modules.utils.TypedXmlSerializer;
34 
35 import org.xmlpull.v1.XmlPullParser;
36 import org.xmlpull.v1.XmlPullParserException;
37 
38 import java.io.Closeable;
39 import java.io.FileDescriptor;
40 import java.io.IOException;
41 import java.io.PrintWriter;
42 import java.io.StringWriter;
43 import java.lang.annotation.Retention;
44 import java.lang.annotation.RetentionPolicy;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Comparator;
48 import java.util.HashMap;
49 import java.util.List;
50 import java.util.Map;
51 
52 /**
53  * Contains a snapshot of battery attribution data, on a per-subsystem and per-UID basis.
54  * <p>
55  * The totals for the entire device are returned as AggregateBatteryConsumers, which can be
56  * obtained by calling {@link #getAggregateBatteryConsumer(int)}.
57  * <p>
58  * Power attributed to individual apps is returned as UidBatteryConsumers, see
59  * {@link #getUidBatteryConsumers()}.
60  *
61  * @hide
62  */
63 @android.ravenwood.annotation.RavenwoodKeepWholeClass
64 public final class BatteryUsageStats implements Parcelable, Closeable {
65 
66     /**
67      * Scope of battery stats included in a BatteryConsumer: the entire device, just
68      * the apps, etc.
69      *
70      * @hide
71      */
72     @IntDef(prefix = {"AGGREGATE_BATTERY_CONSUMER_SCOPE_"}, value = {
73             AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
74             AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
75     })
76     @Retention(RetentionPolicy.SOURCE)
77     public static @interface AggregateBatteryConsumerScope {
78     }
79 
80     /**
81      * Power consumption by the entire device, since last charge.  The power usage in this
82      * scope includes both the power attributed to apps and the power unattributed to any
83      * apps.
84      */
85     public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE = 0;
86 
87     /**
88      * Aggregated power consumed by all applications, combined, since last charge. This is
89      * the sum of power reported in UidBatteryConsumers.
90      */
91     public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS = 1;
92 
93     public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT = 2;
94 
95     // XML tags and attributes for BatteryUsageStats persistence
96     static final String XML_TAG_BATTERY_USAGE_STATS = "battery_usage_stats";
97     static final String XML_TAG_AGGREGATE = "aggregate";
98     static final String XML_TAG_UID = "uid";
99     static final String XML_TAG_USER = "user";
100     static final String XML_TAG_POWER_COMPONENTS = "power_components";
101     static final String XML_TAG_COMPONENT = "component";
102     static final String XML_ATTR_ID = "id";
103     static final String XML_ATTR_UID = "uid";
104     static final String XML_ATTR_USER_ID = "user_id";
105     static final String XML_ATTR_SCOPE = "scope";
106     static final String XML_ATTR_PREFIX_CUSTOM_COMPONENT = "custom_component_";
107     static final String XML_ATTR_PREFIX_INCLUDES_PROC_STATE_DATA = "includes_proc_state_data";
108     static final String XML_ATTR_PREFIX_INCLUDES_SCREEN_STATE_DATA = "includes_screen_state_data";
109     static final String XML_ATTR_PREFIX_INCLUDES_POWER_STATE_DATA = "includes_power_state_data";
110     static final String XML_ATTR_START_TIMESTAMP = "start_timestamp";
111     static final String XML_ATTR_END_TIMESTAMP = "end_timestamp";
112     static final String XML_ATTR_PROCESS_STATE = "process_state";
113     static final String XML_ATTR_SCREEN_STATE = "screen_state";
114     static final String XML_ATTR_POWER_STATE = "power_state";
115     static final String XML_ATTR_POWER = "power";
116     static final String XML_ATTR_DURATION = "duration";
117     static final String XML_ATTR_BATTERY_CAPACITY = "battery_capacity";
118     static final String XML_ATTR_DISCHARGE_PERCENT = "discharge_pct";
119     static final String XML_ATTR_DISCHARGE_LOWER = "discharge_lower";
120     static final String XML_ATTR_DISCHARGE_UPPER = "discharge_upper";
121     static final String XML_ATTR_DISCHARGE_DURATION = "discharge_duration";
122     static final String XML_ATTR_BATTERY_REMAINING = "battery_remaining";
123     static final String XML_ATTR_CHARGE_REMAINING = "charge_remaining";
124     static final String XML_ATTR_HIGHEST_DRAIN_PACKAGE = "highest_drain_package";
125     static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground";
126     static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background";
127     static final String XML_ATTR_TIME_IN_FOREGROUND_SERVICE = "time_in_foreground_service";
128 
129     // Max window size. CursorWindow uses only as much memory as needed.
130     private static final long BATTERY_CONSUMER_CURSOR_WINDOW_SIZE = 20_000_000; // bytes
131 
132     private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
133 
134     private static final int[] UID_USAGE_TIME_PROCESS_STATES = {
135             BatteryConsumer.PROCESS_STATE_FOREGROUND,
136             BatteryConsumer.PROCESS_STATE_BACKGROUND,
137             BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE
138     };
139 
140     private final int mDischargePercentage;
141     private final double mBatteryCapacityMah;
142     private final long mStatsStartTimestampMs;
143     private final long mStatsEndTimestampMs;
144     private final long mStatsDurationMs;
145     private final double mDischargedPowerLowerBound;
146     private final double mDischargedPowerUpperBound;
147     private final long mDischargeDurationMs;
148     private final long mBatteryTimeRemainingMs;
149     private final long mChargeTimeRemainingMs;
150     private final String[] mCustomPowerComponentNames;
151     private final boolean mIncludesProcessStateData;
152     private final boolean mIncludesScreenStateData;
153     private final boolean mIncludesPowerStateData;
154     private final List<UidBatteryConsumer> mUidBatteryConsumers;
155     private final List<UserBatteryConsumer> mUserBatteryConsumers;
156     private final AggregateBatteryConsumer[] mAggregateBatteryConsumers;
157     private final BatteryStatsHistory mBatteryStatsHistory;
158     private final long mPreferredHistoryDurationMs;
159     private final BatteryConsumer.BatteryConsumerDataLayout mBatteryConsumerDataLayout;
160     private CursorWindow mBatteryConsumersCursorWindow;
161 
BatteryUsageStats(@onNull Builder builder)162     private BatteryUsageStats(@NonNull Builder builder) {
163         mStatsStartTimestampMs = builder.mStatsStartTimestampMs;
164         mStatsEndTimestampMs = builder.mStatsEndTimestampMs;
165         mStatsDurationMs = builder.getStatsDuration();
166         mBatteryCapacityMah = builder.mBatteryCapacityMah;
167         mDischargePercentage = builder.mDischargePercentage;
168         mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah;
169         mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah;
170         mDischargeDurationMs = builder.mDischargeDurationMs;
171         mBatteryStatsHistory = builder.mBatteryStatsHistory;
172         mPreferredHistoryDurationMs = builder.mPreferredHistoryDurationMs;
173         mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs;
174         mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs;
175         mCustomPowerComponentNames = builder.mCustomPowerComponentNames;
176         mIncludesProcessStateData = builder.mIncludesProcessStateData;
177         mIncludesScreenStateData = builder.mIncludesScreenStateData;
178         mIncludesPowerStateData = builder.mIncludesPowerStateData;
179         mBatteryConsumerDataLayout = builder.mBatteryConsumerDataLayout;
180         mBatteryConsumersCursorWindow = builder.mBatteryConsumersCursorWindow;
181 
182         double totalPowerMah = 0;
183         final int uidBatteryConsumerCount = builder.mUidBatteryConsumerBuilders.size();
184         mUidBatteryConsumers = new ArrayList<>(uidBatteryConsumerCount);
185         for (int i = 0; i < uidBatteryConsumerCount; i++) {
186             final UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
187                     builder.mUidBatteryConsumerBuilders.valueAt(i);
188             if (!uidBatteryConsumerBuilder.isExcludedFromBatteryUsageStats()) {
189                 final UidBatteryConsumer consumer = uidBatteryConsumerBuilder.build();
190                 totalPowerMah += consumer.getConsumedPower();
191                 mUidBatteryConsumers.add(consumer);
192             }
193         }
194 
195         final int userBatteryConsumerCount = builder.mUserBatteryConsumerBuilders.size();
196         mUserBatteryConsumers = new ArrayList<>(userBatteryConsumerCount);
197         for (int i = 0; i < userBatteryConsumerCount; i++) {
198             final UserBatteryConsumer consumer =
199                     builder.mUserBatteryConsumerBuilders.valueAt(i).build();
200             totalPowerMah += consumer.getConsumedPower();
201             mUserBatteryConsumers.add(consumer);
202         }
203 
204         builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
205                 .addConsumedPower(totalPowerMah);
206 
207         mAggregateBatteryConsumers =
208                 new AggregateBatteryConsumer[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
209         for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) {
210             mAggregateBatteryConsumers[i] = builder.mAggregateBatteryConsumersBuilders[i].build();
211         }
212     }
213 
214     /**
215      * Timestamp (as returned by System.currentTimeMillis()) of the latest battery stats reset, in
216      * milliseconds.
217      */
getStatsStartTimestamp()218     public long getStatsStartTimestamp() {
219         return mStatsStartTimestampMs;
220     }
221 
222     /**
223      * Timestamp (as returned by System.currentTimeMillis()) of when the stats snapshot was taken,
224      * in milliseconds.
225      */
getStatsEndTimestamp()226     public long getStatsEndTimestamp() {
227         return mStatsEndTimestampMs;
228     }
229 
230     /**
231      * Returns the duration of the stats session captured by this BatteryUsageStats.
232      * In rare cases, statsDuration != statsEndTimestamp - statsStartTimestamp.  This may
233      * happen when BatteryUsageStats represents an accumulation of data across multiple
234      * non-contiguous sessions.
235      */
getStatsDuration()236     public long getStatsDuration() {
237         return mStatsDurationMs;
238     }
239 
240     /**
241      * Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully
242      * charged), in mAh
243      */
getConsumedPower()244     public double getConsumedPower() {
245         return mAggregateBatteryConsumers[AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE]
246                 .getConsumedPower();
247     }
248 
249     /**
250      * Returns battery capacity in milli-amp-hours.
251      */
getBatteryCapacity()252     public double getBatteryCapacity() {
253         return mBatteryCapacityMah;
254     }
255 
256     /**
257      * Portion of battery charge drained since BatteryStats reset (e.g. due to being fully
258      * charged), as percentage of the full charge in the range [0:100]. May exceed 100 if
259      * the device repeatedly charged and discharged prior to the reset.
260      */
getDischargePercentage()261     public int getDischargePercentage() {
262         return mDischargePercentage;
263     }
264 
265     /**
266      * Returns the discharged power since BatteryStats were last reset, in mAh as an estimated
267      * range.
268      */
getDischargedPowerRange()269     public Range<Double> getDischargedPowerRange() {
270         return Range.create(mDischargedPowerLowerBound, mDischargedPowerUpperBound);
271     }
272 
273     /**
274      * Returns the total amount of time the battery was discharging.
275      */
getDischargeDurationMs()276     public long getDischargeDurationMs() {
277         return mDischargeDurationMs;
278     }
279 
280     /**
281      * Returns an approximation for how much run time (in milliseconds) is remaining on
282      * the battery.  Returns -1 if no time can be computed: either there is not
283      * enough current data to make a decision, or the battery is currently
284      * charging.
285      */
getBatteryTimeRemainingMs()286     public long getBatteryTimeRemainingMs() {
287         return mBatteryTimeRemainingMs;
288     }
289 
290     /**
291      * Returns an approximation for how much time (in milliseconds) remains until the battery
292      * is fully charged.  Returns -1 if no time can be computed: either there is not
293      * enough current data to make a decision, or the battery is currently discharging.
294      */
getChargeTimeRemainingMs()295     public long getChargeTimeRemainingMs() {
296         return mChargeTimeRemainingMs;
297     }
298 
299     /**
300      * Returns a battery consumer for the specified battery consumer type.
301      */
getAggregateBatteryConsumer( @ggregateBatteryConsumerScope int scope)302     public AggregateBatteryConsumer getAggregateBatteryConsumer(
303             @AggregateBatteryConsumerScope int scope) {
304         return mAggregateBatteryConsumers[scope];
305     }
306 
307     @NonNull
getUidBatteryConsumers()308     public List<UidBatteryConsumer> getUidBatteryConsumers() {
309         return mUidBatteryConsumers;
310     }
311 
312     @NonNull
getUserBatteryConsumers()313     public List<UserBatteryConsumer> getUserBatteryConsumers() {
314         return mUserBatteryConsumers;
315     }
316 
317     /**
318      * Returns the names of custom power components in order, so the first name in the array
319      * corresponds to the custom componentId
320      * {@link BatteryConsumer#FIRST_CUSTOM_POWER_COMPONENT_ID}.
321      */
322     @NonNull
getCustomPowerComponentNames()323     public String[] getCustomPowerComponentNames() {
324         return mCustomPowerComponentNames;
325     }
326 
isProcessStateDataIncluded()327     public boolean isProcessStateDataIncluded() {
328         return mIncludesProcessStateData;
329     }
330 
331     /**
332      * Returns an iterator for {@link android.os.BatteryStats.HistoryItem}'s.
333      */
334     @NonNull
iterateBatteryStatsHistory()335     public BatteryStatsHistoryIterator iterateBatteryStatsHistory() {
336         if (mBatteryStatsHistory == null) {
337             throw new IllegalStateException(
338                     "Battery history was not requested in the BatteryUsageStatsQuery");
339         }
340         return new BatteryStatsHistoryIterator(mBatteryStatsHistory, 0, MonotonicClock.UNDEFINED);
341     }
342 
343     @Override
describeContents()344     public int describeContents() {
345         return 0;
346     }
347 
BatteryUsageStats(@onNull Parcel source)348     private BatteryUsageStats(@NonNull Parcel source) {
349         mStatsStartTimestampMs = source.readLong();
350         mStatsEndTimestampMs = source.readLong();
351         mStatsDurationMs = source.readLong();
352         mBatteryCapacityMah = source.readDouble();
353         mDischargePercentage = source.readInt();
354         mDischargedPowerLowerBound = source.readDouble();
355         mDischargedPowerUpperBound = source.readDouble();
356         mDischargeDurationMs = source.readLong();
357         mBatteryTimeRemainingMs = source.readLong();
358         mChargeTimeRemainingMs = source.readLong();
359         mCustomPowerComponentNames = source.readStringArray();
360         mIncludesProcessStateData = source.readBoolean();
361         mIncludesScreenStateData = source.readBoolean();
362         mIncludesPowerStateData = source.readBoolean();
363 
364         mBatteryConsumersCursorWindow = CursorWindow.newFromParcel(source);
365         mBatteryConsumerDataLayout = BatteryConsumer.createBatteryConsumerDataLayout(
366                 mCustomPowerComponentNames, mIncludesProcessStateData,
367                 mIncludesScreenStateData, mIncludesPowerStateData);
368 
369         final int numRows = mBatteryConsumersCursorWindow.getNumRows();
370 
371         mAggregateBatteryConsumers =
372                 new AggregateBatteryConsumer[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
373         mUidBatteryConsumers = new ArrayList<>(numRows);
374         mUserBatteryConsumers = new ArrayList<>();
375 
376         for (int i = 0; i < numRows; i++) {
377             final BatteryConsumer.BatteryConsumerData data =
378                     new BatteryConsumer.BatteryConsumerData(mBatteryConsumersCursorWindow, i,
379                             mBatteryConsumerDataLayout);
380 
381             int consumerType = mBatteryConsumersCursorWindow.getInt(i,
382                             BatteryConsumer.COLUMN_INDEX_BATTERY_CONSUMER_TYPE);
383             switch (consumerType) {
384                 case AggregateBatteryConsumer.CONSUMER_TYPE_AGGREGATE: {
385                     final AggregateBatteryConsumer consumer = new AggregateBatteryConsumer(data);
386                     mAggregateBatteryConsumers[consumer.getScope()] = consumer;
387                     break;
388                 }
389                 case UidBatteryConsumer.CONSUMER_TYPE_UID: {
390                     mUidBatteryConsumers.add(new UidBatteryConsumer(data));
391                     break;
392                 }
393                 case UserBatteryConsumer.CONSUMER_TYPE_USER:
394                     mUserBatteryConsumers.add(new UserBatteryConsumer(data));
395                     break;
396             }
397         }
398 
399         if (source.readBoolean()) {
400             mBatteryStatsHistory = BatteryStatsHistory.createFromBatteryUsageStatsParcel(source);
401             mPreferredHistoryDurationMs = source.readLong();
402         } else {
403             mBatteryStatsHistory = null;
404             mPreferredHistoryDurationMs = 0;
405         }
406     }
407 
408     @Override
writeToParcel(@onNull Parcel dest, int flags)409     public void writeToParcel(@NonNull Parcel dest, int flags) {
410         dest.writeLong(mStatsStartTimestampMs);
411         dest.writeLong(mStatsEndTimestampMs);
412         dest.writeLong(mStatsDurationMs);
413         dest.writeDouble(mBatteryCapacityMah);
414         dest.writeInt(mDischargePercentage);
415         dest.writeDouble(mDischargedPowerLowerBound);
416         dest.writeDouble(mDischargedPowerUpperBound);
417         dest.writeLong(mDischargeDurationMs);
418         dest.writeLong(mBatteryTimeRemainingMs);
419         dest.writeLong(mChargeTimeRemainingMs);
420         dest.writeStringArray(mCustomPowerComponentNames);
421         dest.writeBoolean(mIncludesProcessStateData);
422         dest.writeBoolean(mIncludesScreenStateData);
423         dest.writeBoolean(mIncludesPowerStateData);
424 
425         mBatteryConsumersCursorWindow.writeToParcel(dest, flags);
426 
427         if (mBatteryStatsHistory != null) {
428             dest.writeBoolean(true);
429             mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest, mPreferredHistoryDurationMs);
430         } else {
431             dest.writeBoolean(false);
432         }
433     }
434 
435     @NonNull
436     public static final Creator<BatteryUsageStats> CREATOR = new Creator<BatteryUsageStats>() {
437         public BatteryUsageStats createFromParcel(@NonNull Parcel source) {
438             return new BatteryUsageStats(source);
439         }
440 
441         public BatteryUsageStats[] newArray(int size) {
442             return new BatteryUsageStats[size];
443         }
444     };
445 
446     /** Returns a proto (as used for atoms.proto) corresponding to this BatteryUsageStats. */
getStatsProto()447     public byte[] getStatsProto() {
448         // ProtoOutputStream.getRawSize() returns the buffer size before compaction.
449         // BatteryUsageStats contains a lot of integers, so compaction of integers to
450         // varint reduces the size of the proto buffer by as much as 50%.
451         int maxRawSize = (int) (STATSD_PULL_ATOM_MAX_BYTES * 1.75);
452         // Limit the number of attempts in order to prevent an infinite loop
453         for (int i = 0; i < 3; i++) {
454             final ProtoOutputStream proto = new ProtoOutputStream();
455             writeStatsProto(proto, maxRawSize);
456 
457             final int rawSize = proto.getRawSize();
458             final byte[] protoOutput = proto.getBytes();
459 
460             if (protoOutput.length <= STATSD_PULL_ATOM_MAX_BYTES) {
461                 return protoOutput;
462             }
463 
464             // Adjust maxRawSize proportionately and try again.
465             maxRawSize =
466                     (int) ((long) STATSD_PULL_ATOM_MAX_BYTES * rawSize / protoOutput.length - 1024);
467         }
468 
469         // Fallback: if we have failed to generate a proto smaller than STATSD_PULL_ATOM_MAX_BYTES,
470         // just generate a proto with the _rawSize_ of STATSD_PULL_ATOM_MAX_BYTES, which is
471         // guaranteed to produce a compacted proto (significantly) smaller than
472         // STATSD_PULL_ATOM_MAX_BYTES.
473         final ProtoOutputStream proto = new ProtoOutputStream();
474         writeStatsProto(proto, STATSD_PULL_ATOM_MAX_BYTES);
475         return proto.getBytes();
476     }
477 
478     /**
479      * Writes contents in a binary protobuffer format, using
480      * the android.os.BatteryUsageStatsAtomsProto proto.
481      */
dumpToProto(FileDescriptor fd)482     public void dumpToProto(FileDescriptor fd) {
483         final ProtoOutputStream proto = new ProtoOutputStream(fd);
484         writeStatsProto(proto, /* max size */ Integer.MAX_VALUE);
485         proto.flush();
486     }
487 
488     @NonNull
writeStatsProto(ProtoOutputStream proto, int maxRawSize)489     private void writeStatsProto(ProtoOutputStream proto, int maxRawSize) {
490         final AggregateBatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer(
491                 AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
492 
493         proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, getStatsStartTimestamp());
494         proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, getStatsEndTimestamp());
495         proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, getStatsDuration());
496         proto.write(BatteryUsageStatsAtomsProto.SESSION_DISCHARGE_PERCENTAGE,
497                 getDischargePercentage());
498         proto.write(BatteryUsageStatsAtomsProto.DISCHARGE_DURATION_MILLIS,
499                 getDischargeDurationMs());
500         deviceBatteryConsumer.writeStatsProto(proto,
501                 BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER);
502         writeUidBatteryConsumersProto(proto, maxRawSize);
503     }
504 
505     /**
506      * Writes the UidBatteryConsumers data, held by this BatteryUsageStats, to the proto (as used
507      * for atoms.proto).
508      */
writeUidBatteryConsumersProto(ProtoOutputStream proto, int maxRawSize)509     private void writeUidBatteryConsumersProto(ProtoOutputStream proto, int maxRawSize) {
510         final List<UidBatteryConsumer> consumers = getUidBatteryConsumers();
511         // Order consumers by descending weight (a combination of consumed power and usage time)
512         consumers.sort(Comparator.comparingDouble(this::getUidBatteryConsumerWeight).reversed());
513 
514         final int size = consumers.size();
515         for (int i = 0; i < size; i++) {
516             final UidBatteryConsumer consumer = consumers.get(i);
517 
518             final long fgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
519             final long bgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
520             final boolean hasBaseData = consumer.hasStatsProtoData();
521 
522             if (fgMs == 0 && bgMs == 0 && !hasBaseData) {
523                 continue;
524             }
525 
526             final long token = proto.start(BatteryUsageStatsAtomsProto.UID_BATTERY_CONSUMERS);
527             proto.write(
528                     BatteryUsageStatsAtomsProto.UidBatteryConsumer.UID,
529                     consumer.getUid());
530             if (hasBaseData) {
531                 consumer.writeStatsProto(proto,
532                         BatteryUsageStatsAtomsProto.UidBatteryConsumer.BATTERY_CONSUMER_DATA);
533             }
534             proto.write(
535                     BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_FOREGROUND_MILLIS,
536                     fgMs);
537             proto.write(
538                     BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_BACKGROUND_MILLIS,
539                     bgMs);
540             for (int processState : UID_USAGE_TIME_PROCESS_STATES) {
541                 final long timeInStateMillis = consumer.getTimeInProcessStateMs(processState);
542                 if (timeInStateMillis <= 0) {
543                     continue;
544                 }
545                 final long timeInStateToken = proto.start(
546                         BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_STATE);
547                 proto.write(
548                         BatteryUsageStatsAtomsProto.UidBatteryConsumer.TimeInState.PROCESS_STATE,
549                         processState);
550                 proto.write(
551                         BatteryUsageStatsAtomsProto.UidBatteryConsumer.TimeInState
552                                 .TIME_IN_STATE_MILLIS,
553                         timeInStateMillis);
554                 proto.end(timeInStateToken);
555             }
556             proto.end(token);
557 
558             if (proto.getRawSize() >= maxRawSize) {
559                 break;
560             }
561         }
562     }
563 
564     private static final double WEIGHT_CONSUMED_POWER = 1;
565     // Weight one hour in foreground the same as 100 mAh of power drain
566     private static final double WEIGHT_FOREGROUND_STATE = 100.0 / (1 * 60 * 60 * 1000);
567     // Weight one hour in background the same as 300 mAh of power drain
568     private static final double WEIGHT_BACKGROUND_STATE = 300.0 / (1 * 60 * 60 * 1000);
569 
570     /**
571      * Computes the weight associated with a UidBatteryConsumer, which is used for sorting.
572      * We want applications with the largest consumed power as well as applications
573      * with the highest usage time to be included in the statsd atom.
574      */
getUidBatteryConsumerWeight(UidBatteryConsumer uidBatteryConsumer)575     private double getUidBatteryConsumerWeight(UidBatteryConsumer uidBatteryConsumer) {
576         final double consumedPower = uidBatteryConsumer.getConsumedPower();
577         final long timeInForeground =
578                 uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
579         final long timeInBackground =
580                 uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
581         return consumedPower * WEIGHT_CONSUMED_POWER
582                 + timeInForeground * WEIGHT_FOREGROUND_STATE
583                 + timeInBackground * WEIGHT_BACKGROUND_STATE;
584     }
585 
586     /**
587      * Prints the stats in a human-readable format.
588      */
dump(PrintWriter pw, String prefix)589     public void dump(PrintWriter pw, String prefix) {
590         pw.print(prefix);
591         pw.println("  Estimated power use (mAh):");
592         pw.print(prefix);
593         pw.print("    Capacity: ");
594         pw.print(BatteryStats.formatCharge(getBatteryCapacity()));
595         pw.print(", Computed drain: ");
596         pw.print(BatteryStats.formatCharge(getConsumedPower()));
597         final Range<Double> dischargedPowerRange = getDischargedPowerRange();
598         pw.print(", actual drain: ");
599         pw.print(BatteryStats.formatCharge(dischargedPowerRange.getLower()));
600         if (!dischargedPowerRange.getLower().equals(dischargedPowerRange.getUpper())) {
601             pw.print("-");
602             pw.print(BatteryStats.formatCharge(dischargedPowerRange.getUpper()));
603         }
604         pw.println();
605 
606         pw.println("    Global");
607         final BatteryConsumer deviceConsumer = getAggregateBatteryConsumer(
608                 AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
609         final BatteryConsumer appsConsumer = getAggregateBatteryConsumer(
610                 AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
611 
612         for (@BatteryConsumer.PowerComponentId int powerComponent :
613                 mBatteryConsumerDataLayout.powerComponentIds) {
614             final double devicePowerMah = deviceConsumer.getConsumedPower(powerComponent);
615             final double appsPowerMah = appsConsumer.getConsumedPower(powerComponent);
616             if (devicePowerMah == 0 && appsPowerMah == 0) {
617                 continue;
618             }
619 
620             printPowerComponent(pw, prefix,
621                     mBatteryConsumerDataLayout.getPowerComponentName(powerComponent),
622                     devicePowerMah, appsPowerMah,
623                     deviceConsumer.getUsageDurationMillis(powerComponent));
624         }
625 
626         String prefixPlus = prefix + "  ";
627         if (mIncludesPowerStateData && !mIncludesScreenStateData) {
628             for (@BatteryConsumer.PowerState int powerState = 0;
629                     powerState < BatteryConsumer.POWER_STATE_COUNT;
630                     powerState++) {
631                 if (powerState != BatteryConsumer.POWER_STATE_UNSPECIFIED) {
632                     dumpPowerComponents(pw, BatteryConsumer.SCREEN_STATE_ANY, powerState,
633                             prefixPlus);
634                 }
635             }
636         } else if (!mIncludesPowerStateData && mIncludesScreenStateData) {
637             for (@BatteryConsumer.ScreenState int screenState = 0;
638                     screenState < BatteryConsumer.SCREEN_STATE_COUNT;
639                     screenState++) {
640                 if (screenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
641                     dumpPowerComponents(pw, screenState, BatteryConsumer.POWER_STATE_ANY,
642                             prefixPlus);
643                 }
644             }
645         } else if (mIncludesPowerStateData && mIncludesScreenStateData) {
646             for (@BatteryConsumer.PowerState int powerState = 0;
647                     powerState < BatteryConsumer.POWER_STATE_COUNT;
648                     powerState++) {
649                 if (powerState != BatteryConsumer.POWER_STATE_UNSPECIFIED) {
650                     for (@BatteryConsumer.ScreenState int screenState = 0;
651                             screenState < BatteryConsumer.SCREEN_STATE_COUNT; screenState++) {
652                         if (screenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
653                             dumpPowerComponents(pw, screenState, powerState, prefixPlus);
654                         }
655                     }
656                 }
657             }
658         }
659 
660         dumpSortedBatteryConsumers(pw, prefix, getUidBatteryConsumers());
661         dumpSortedBatteryConsumers(pw, prefix, getUserBatteryConsumers());
662         pw.println();
663     }
664 
dumpPowerComponents(PrintWriter pw, @BatteryConsumer.ScreenState int screenState, @BatteryConsumer.PowerState int powerState, String prefix)665     private void dumpPowerComponents(PrintWriter pw,
666             @BatteryConsumer.ScreenState int screenState,
667             @BatteryConsumer.PowerState int powerState, String prefix) {
668         final BatteryConsumer deviceConsumer = getAggregateBatteryConsumer(
669                 AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
670         final BatteryConsumer appsConsumer = getAggregateBatteryConsumer(
671                 AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
672 
673         boolean labelPrinted = false;
674         for (@BatteryConsumer.PowerComponentId int powerComponent :
675                 mBatteryConsumerDataLayout.powerComponentIds) {
676             BatteryConsumer.Dimensions dimensions = new BatteryConsumer.Dimensions(
677                     powerComponent, BatteryConsumer.PROCESS_STATE_ANY, screenState, powerState);
678             final double devicePowerMah = deviceConsumer.getConsumedPower(dimensions);
679             final double appsPowerMah = appsConsumer.getConsumedPower(dimensions);
680             if (devicePowerMah == 0 && appsPowerMah == 0) {
681                 continue;
682             }
683 
684             if (!labelPrinted) {
685                 boolean empty = true;
686                 StringBuilder stateLabel = new StringBuilder();
687                 stateLabel.append("      (");
688                 if (powerState != BatteryConsumer.POWER_STATE_ANY) {
689                     stateLabel.append(BatteryConsumer.powerStateToString(powerState));
690                     empty = false;
691                 }
692                 if (screenState != BatteryConsumer.SCREEN_STATE_ANY) {
693                     if (!empty) {
694                         stateLabel.append(", ");
695                     }
696                     stateLabel.append("screen ")
697                             .append(BatteryConsumer.screenStateToString(screenState));
698                     empty = false;
699                 }
700                 if (!empty) {
701                     stateLabel.append(")");
702                     pw.println(stateLabel);
703                     labelPrinted = true;
704                 }
705             }
706             printPowerComponent(pw, prefix,
707                     mBatteryConsumerDataLayout.getPowerComponentName(powerComponent),
708                     devicePowerMah, appsPowerMah,
709                     deviceConsumer.getUsageDurationMillis(dimensions));
710         }
711     }
712 
printPowerComponent(PrintWriter pw, String prefix, String label, double devicePowerMah, double appsPowerMah, long durationMs)713     private void printPowerComponent(PrintWriter pw, String prefix, String label,
714             double devicePowerMah, double appsPowerMah, long durationMs) {
715         StringBuilder sb = new StringBuilder();
716         sb.append(prefix).append("    ").append(label).append(": ")
717                 .append(BatteryStats.formatCharge(devicePowerMah));
718         sb.append(" apps: ").append(BatteryStats.formatCharge(appsPowerMah));
719         if (durationMs != 0) {
720             sb.append(" duration: ");
721             BatteryStats.formatTimeMs(sb, durationMs);
722         }
723 
724         pw.println(sb);
725     }
726 
dumpSortedBatteryConsumers(PrintWriter pw, String prefix, List<? extends BatteryConsumer> batteryConsumers)727     private void dumpSortedBatteryConsumers(PrintWriter pw, String prefix,
728             List<? extends BatteryConsumer> batteryConsumers) {
729         batteryConsumers.sort(
730                 Comparator.<BatteryConsumer>comparingDouble(BatteryConsumer::getConsumedPower)
731                         .reversed());
732         for (BatteryConsumer consumer : batteryConsumers) {
733             if (consumer.getConsumedPower() == 0) {
734                 continue;
735             }
736             pw.print(prefix);
737             pw.print("  ");
738             consumer.dump(pw);
739         }
740     }
741 
742     /** Serializes this object to XML */
writeXml(TypedXmlSerializer serializer)743     public void writeXml(TypedXmlSerializer serializer) throws IOException {
744         serializer.startTag(null, XML_TAG_BATTERY_USAGE_STATS);
745 
746         for (int i = 0; i < mCustomPowerComponentNames.length; i++) {
747             serializer.attribute(null, XML_ATTR_PREFIX_CUSTOM_COMPONENT + i,
748                     mCustomPowerComponentNames[i]);
749         }
750         serializer.attributeBoolean(null, XML_ATTR_PREFIX_INCLUDES_PROC_STATE_DATA,
751                 mIncludesProcessStateData);
752         serializer.attributeBoolean(null, XML_ATTR_PREFIX_INCLUDES_SCREEN_STATE_DATA,
753                 mIncludesScreenStateData);
754         serializer.attributeBoolean(null, XML_ATTR_PREFIX_INCLUDES_POWER_STATE_DATA,
755                 mIncludesPowerStateData);
756         serializer.attributeLong(null, XML_ATTR_START_TIMESTAMP, mStatsStartTimestampMs);
757         serializer.attributeLong(null, XML_ATTR_END_TIMESTAMP, mStatsEndTimestampMs);
758         serializer.attributeLong(null, XML_ATTR_DURATION, mStatsDurationMs);
759         serializer.attributeDouble(null, XML_ATTR_BATTERY_CAPACITY, mBatteryCapacityMah);
760         serializer.attributeInt(null, XML_ATTR_DISCHARGE_PERCENT, mDischargePercentage);
761         serializer.attributeDouble(null, XML_ATTR_DISCHARGE_LOWER, mDischargedPowerLowerBound);
762         serializer.attributeDouble(null, XML_ATTR_DISCHARGE_UPPER, mDischargedPowerUpperBound);
763         serializer.attributeLong(null, XML_ATTR_DISCHARGE_DURATION, mDischargeDurationMs);
764         serializer.attributeLong(null, XML_ATTR_BATTERY_REMAINING, mBatteryTimeRemainingMs);
765         serializer.attributeLong(null, XML_ATTR_CHARGE_REMAINING, mChargeTimeRemainingMs);
766 
767         for (int scope = 0; scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
768                 scope++) {
769             mAggregateBatteryConsumers[scope].writeToXml(serializer, scope);
770         }
771         for (UidBatteryConsumer consumer : mUidBatteryConsumers) {
772             consumer.writeToXml(serializer);
773         }
774         for (UserBatteryConsumer consumer : mUserBatteryConsumers) {
775             consumer.writeToXml(serializer);
776         }
777         serializer.endTag(null, XML_TAG_BATTERY_USAGE_STATS);
778     }
779 
780     /** Parses an XML representation of BatteryUsageStats */
createFromXml(TypedXmlPullParser parser)781     public static BatteryUsageStats createFromXml(TypedXmlPullParser parser)
782             throws XmlPullParserException, IOException {
783         return createBuilderFromXml(parser).build();
784     }
785 
786     /** Parses an XML representation of BatteryUsageStats */
createBuilderFromXml(TypedXmlPullParser parser)787     public static BatteryUsageStats.Builder createBuilderFromXml(TypedXmlPullParser parser)
788             throws XmlPullParserException, IOException {
789         Builder builder = null;
790         int eventType = parser.getEventType();
791         while (eventType != XmlPullParser.END_DOCUMENT) {
792             if (eventType == XmlPullParser.START_TAG
793                     && parser.getName().equals(XML_TAG_BATTERY_USAGE_STATS)) {
794                 List<String> customComponentNames = new ArrayList<>();
795                 int i = 0;
796                 while (true) {
797                     int index = parser.getAttributeIndex(null,
798                             XML_ATTR_PREFIX_CUSTOM_COMPONENT + i);
799                     if (index == -1) {
800                         break;
801                     }
802                     customComponentNames.add(parser.getAttributeValue(index));
803                     i++;
804                 }
805 
806                 final boolean includesProcStateData = parser.getAttributeBoolean(null,
807                         XML_ATTR_PREFIX_INCLUDES_PROC_STATE_DATA, false);
808                 final boolean includesScreenStateData = parser.getAttributeBoolean(null,
809                         XML_ATTR_PREFIX_INCLUDES_SCREEN_STATE_DATA, false);
810                 final boolean includesPowerStateData = parser.getAttributeBoolean(null,
811                         XML_ATTR_PREFIX_INCLUDES_POWER_STATE_DATA, false);
812 
813                 builder = new Builder(customComponentNames.toArray(new String[0]),
814                         includesProcStateData, includesScreenStateData, includesPowerStateData, 0);
815 
816                 builder.setStatsStartTimestamp(
817                         parser.getAttributeLong(null, XML_ATTR_START_TIMESTAMP));
818                 builder.setStatsEndTimestamp(
819                         parser.getAttributeLong(null, XML_ATTR_END_TIMESTAMP));
820                 builder.setStatsDuration(
821                         parser.getAttributeLong(null, XML_ATTR_DURATION));
822                 builder.setBatteryCapacity(
823                         parser.getAttributeDouble(null, XML_ATTR_BATTERY_CAPACITY));
824                 builder.addDischargePercentage(
825                         parser.getAttributeInt(null, XML_ATTR_DISCHARGE_PERCENT));
826                 builder.addDischargedPowerRange(
827                         parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_LOWER),
828                         parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_UPPER));
829                 builder.addDischargeDurationMs(
830                         parser.getAttributeLong(null, XML_ATTR_DISCHARGE_DURATION));
831                 builder.setBatteryTimeRemainingMs(
832                         parser.getAttributeLong(null, XML_ATTR_BATTERY_REMAINING));
833                 builder.setChargeTimeRemainingMs(
834                         parser.getAttributeLong(null, XML_ATTR_CHARGE_REMAINING));
835 
836                 eventType = parser.next();
837                 break;
838             }
839             eventType = parser.next();
840         }
841 
842         if (builder == null) {
843             throw new XmlPullParserException("No root element");
844         }
845 
846         while (eventType != XmlPullParser.END_DOCUMENT) {
847             if (eventType == XmlPullParser.START_TAG) {
848                 switch (parser.getName()) {
849                     case XML_TAG_AGGREGATE:
850                         AggregateBatteryConsumer.parseXml(parser, builder);
851                         break;
852                     case XML_TAG_UID:
853                         UidBatteryConsumer.createFromXml(parser, builder);
854                         break;
855                     case XML_TAG_USER:
856                         UserBatteryConsumer.createFromXml(parser, builder);
857                         break;
858                 }
859             }
860             eventType = parser.next();
861         }
862 
863         return builder;
864     }
865 
866     @Override
close()867     public void close() throws IOException {
868         onCursorWindowReleased(mBatteryConsumersCursorWindow);
869         mBatteryConsumersCursorWindow.close();
870         mBatteryConsumersCursorWindow = null;
871     }
872 
873     @Override
finalize()874     protected void finalize() throws Throwable {
875         if (mBatteryConsumersCursorWindow != null) {
876             // Do not decrement sOpenCusorWindowCount. All instances should be closed explicitly
877             mBatteryConsumersCursorWindow.close();
878         }
879         super.finalize();
880     }
881 
882     @Override
toString()883     public String toString() {
884         StringWriter sw = new StringWriter();
885         PrintWriter pw = new PrintWriter(sw);
886         dump(pw, "");
887         pw.flush();
888         return sw.toString();
889     }
890 
891     /**
892      * Builder for BatteryUsageStats.
893      */
894     public static final class Builder {
895         private final CursorWindow mBatteryConsumersCursorWindow;
896         @NonNull
897         private final String[] mCustomPowerComponentNames;
898         private final boolean mIncludesProcessStateData;
899         private final boolean mIncludesScreenStateData;
900         private final boolean mIncludesPowerStateData;
901         private final double mMinConsumedPowerThreshold;
902         private final BatteryConsumer.BatteryConsumerDataLayout mBatteryConsumerDataLayout;
903         private long mStatsStartTimestampMs;
904         private long mStatsEndTimestampMs;
905         private long mStatsDurationMs = -1;
906         private double mBatteryCapacityMah;
907         private int mDischargePercentage;
908         private double mDischargedPowerLowerBoundMah;
909         private double mDischargedPowerUpperBoundMah;
910         private long mDischargeDurationMs;
911         private long mBatteryTimeRemainingMs = -1;
912         private long mChargeTimeRemainingMs = -1;
913         private final AggregateBatteryConsumer.Builder[] mAggregateBatteryConsumersBuilders =
914                 new AggregateBatteryConsumer.Builder[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
915         private final SparseArray<UidBatteryConsumer.Builder> mUidBatteryConsumerBuilders =
916                 new SparseArray<>();
917         private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders =
918                 new SparseArray<>();
919         private BatteryStatsHistory mBatteryStatsHistory;
920         private long mPreferredHistoryDurationMs;
921 
Builder(@onNull String[] customPowerComponentNames)922         public Builder(@NonNull String[] customPowerComponentNames) {
923             this(customPowerComponentNames, false, false, false, 0);
924         }
925 
Builder(@onNull String[] customPowerComponentNames, boolean includeProcessStateData, boolean includeScreenStateData, boolean includesPowerStateData, double minConsumedPowerThreshold)926         public Builder(@NonNull String[] customPowerComponentNames,
927                 boolean includeProcessStateData, boolean includeScreenStateData,
928                 boolean includesPowerStateData, double minConsumedPowerThreshold) {
929             mBatteryConsumersCursorWindow =
930                     new CursorWindow(null, BATTERY_CONSUMER_CURSOR_WINDOW_SIZE);
931             onCursorWindowAllocated(mBatteryConsumersCursorWindow);
932             mBatteryConsumerDataLayout = BatteryConsumer.createBatteryConsumerDataLayout(
933                     customPowerComponentNames, includeProcessStateData,
934                     includeScreenStateData, includesPowerStateData);
935             mBatteryConsumersCursorWindow.setNumColumns(mBatteryConsumerDataLayout.columnCount);
936 
937             mCustomPowerComponentNames = customPowerComponentNames;
938             mIncludesProcessStateData = includeProcessStateData;
939             mIncludesScreenStateData = includeScreenStateData;
940             mIncludesPowerStateData = includesPowerStateData;
941             mMinConsumedPowerThreshold = minConsumedPowerThreshold;
942             for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) {
943                 final BatteryConsumer.BatteryConsumerData data =
944                         BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
945                                 mBatteryConsumerDataLayout);
946                 mAggregateBatteryConsumersBuilders[scope] =
947                         new AggregateBatteryConsumer.Builder(
948                                 data, scope, mMinConsumedPowerThreshold);
949             }
950         }
951 
isProcessStateDataNeeded()952         public boolean isProcessStateDataNeeded() {
953             return mIncludesProcessStateData;
954         }
955 
isScreenStateDataNeeded()956         public boolean isScreenStateDataNeeded() {
957             return mIncludesScreenStateData;
958         }
959 
isPowerStateDataNeeded()960         public boolean isPowerStateDataNeeded() {
961             return mIncludesPowerStateData;
962         }
963 
964         /**
965          * Returns true if this Builder is configured to hold data for the specified
966          * power component index.
967          */
isSupportedPowerComponent( @atteryConsumer.PowerComponentId int componentId)968         public boolean isSupportedPowerComponent(
969                 @BatteryConsumer.PowerComponentId int componentId) {
970             return componentId < BatteryConsumer.POWER_COMPONENT_COUNT
971                     || (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
972                     && componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
973                     + mBatteryConsumerDataLayout.customPowerComponentCount);
974         }
975 
976         /**
977          * Constructs a read-only object using the Builder values.
978          */
979         @NonNull
build()980         public BatteryUsageStats build() {
981             if (mBatteryConsumersCursorWindow == null) {
982                 throw new IllegalStateException("Builder has been discarded");
983             }
984             return new BatteryUsageStats(this);
985         }
986 
987         /**
988          * Close this builder without actually calling ".build()". Do not attempt
989          * to continue using the builder after this call.
990          */
discard()991         public void discard() {
992             mBatteryConsumersCursorWindow.close();
993             onCursorWindowReleased(mBatteryConsumersCursorWindow);
994         }
995 
996         /**
997          * Sets the battery capacity in milli-amp-hours.
998          */
setBatteryCapacity(double batteryCapacityMah)999         public Builder setBatteryCapacity(double batteryCapacityMah) {
1000             mBatteryCapacityMah = batteryCapacityMah;
1001             return this;
1002         }
1003 
1004         /**
1005          * Sets the timestamp of the latest battery stats reset, in milliseconds.
1006          */
setStatsStartTimestamp(long statsStartTimestampMs)1007         public Builder setStatsStartTimestamp(long statsStartTimestampMs) {
1008             mStatsStartTimestampMs = statsStartTimestampMs;
1009             return this;
1010         }
1011 
1012         /**
1013          * Sets the timestamp of when the battery stats snapshot was taken, in milliseconds.
1014          */
setStatsEndTimestamp(long statsEndTimestampMs)1015         public Builder setStatsEndTimestamp(long statsEndTimestampMs) {
1016             mStatsEndTimestampMs = statsEndTimestampMs;
1017             return this;
1018         }
1019 
1020         /**
1021          * Sets the duration of the stats session.  The default value of this field is
1022          * statsEndTimestamp - statsStartTimestamp.
1023          */
setStatsDuration(long statsDurationMs)1024         public Builder setStatsDuration(long statsDurationMs) {
1025             mStatsDurationMs = statsDurationMs;
1026             return this;
1027         }
1028 
1029         /**
1030          * Returns the duration of the battery session reflected by these stats.
1031          */
getStatsDuration()1032         public long getStatsDuration() {
1033             if (mStatsDurationMs != -1) {
1034                 return mStatsDurationMs;
1035             } else {
1036                 return mStatsEndTimestampMs - mStatsStartTimestampMs;
1037             }
1038         }
1039 
1040         /**
1041          * Accumulates the battery discharge amount as percentage of the full charge. Can exceed 100
1042          */
1043         @NonNull
addDischargePercentage(int dischargePercentage)1044         public Builder addDischargePercentage(int dischargePercentage) {
1045             mDischargePercentage += dischargePercentage;
1046             return this;
1047         }
1048 
1049         /**
1050          * Accumulates the estimated battery discharge range.
1051          */
1052         @NonNull
addDischargedPowerRange(double dischargedPowerLowerBoundMah, double dischargedPowerUpperBoundMah)1053         public Builder addDischargedPowerRange(double dischargedPowerLowerBoundMah,
1054                 double dischargedPowerUpperBoundMah) {
1055             mDischargedPowerLowerBoundMah += dischargedPowerLowerBoundMah;
1056             mDischargedPowerUpperBoundMah += dischargedPowerUpperBoundMah;
1057             return this;
1058         }
1059 
1060         /**
1061          * Sets the total battery discharge time, in milliseconds.
1062          */
1063         @NonNull
addDischargeDurationMs(long durationMs)1064         public Builder addDischargeDurationMs(long durationMs) {
1065             mDischargeDurationMs += durationMs;
1066             return this;
1067         }
1068 
1069         /**
1070          * Sets an approximation for how much time (in milliseconds) remains until the battery
1071          * is fully discharged.
1072          */
1073         @NonNull
setBatteryTimeRemainingMs(long batteryTimeRemainingMs)1074         public Builder setBatteryTimeRemainingMs(long batteryTimeRemainingMs) {
1075             mBatteryTimeRemainingMs = batteryTimeRemainingMs;
1076             return this;
1077         }
1078 
1079         /**
1080          * Sets an approximation for how much time (in milliseconds) remains until the battery
1081          * is fully charged.
1082          */
1083         @NonNull
setChargeTimeRemainingMs(long chargeTimeRemainingMs)1084         public Builder setChargeTimeRemainingMs(long chargeTimeRemainingMs) {
1085             mChargeTimeRemainingMs = chargeTimeRemainingMs;
1086             return this;
1087         }
1088 
1089         /**
1090          * Sets the parceled recent history.
1091          */
1092         @NonNull
setBatteryHistory(BatteryStatsHistory batteryStatsHistory, long preferredHistoryDurationMs)1093         public Builder setBatteryHistory(BatteryStatsHistory batteryStatsHistory,
1094                 long preferredHistoryDurationMs) {
1095             mBatteryStatsHistory = batteryStatsHistory;
1096             mPreferredHistoryDurationMs = preferredHistoryDurationMs;
1097             return this;
1098         }
1099 
1100         /**
1101          * Creates or returns an AggregateBatteryConsumer builder, which represents aggregate
1102          * battery consumption data for the specified scope.
1103          */
1104         @NonNull
getAggregateBatteryConsumerBuilder( @ggregateBatteryConsumerScope int scope)1105         public AggregateBatteryConsumer.Builder getAggregateBatteryConsumerBuilder(
1106                 @AggregateBatteryConsumerScope int scope) {
1107             return mAggregateBatteryConsumersBuilders[scope];
1108         }
1109 
1110         /**
1111          * Creates or returns a UidBatteryConsumer, which represents battery attribution
1112          * data for an individual UID.
1113          */
1114         @NonNull
getOrCreateUidBatteryConsumerBuilder( @onNull BatteryStats.Uid batteryStatsUid)1115         public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder(
1116                 @NonNull BatteryStats.Uid batteryStatsUid) {
1117             int uid = batteryStatsUid.getUid();
1118             UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid);
1119             if (builder == null) {
1120                 final BatteryConsumer.BatteryConsumerData data =
1121                         BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
1122                                 mBatteryConsumerDataLayout);
1123                 builder = new UidBatteryConsumer.Builder(data, batteryStatsUid,
1124                         mMinConsumedPowerThreshold);
1125                 mUidBatteryConsumerBuilders.put(uid, builder);
1126             }
1127             return builder;
1128         }
1129 
1130         /**
1131          * Creates or returns a UidBatteryConsumer, which represents battery attribution
1132          * data for an individual UID. This version of the method is not suitable for use
1133          * with PowerCalculators.
1134          */
1135         @NonNull
getOrCreateUidBatteryConsumerBuilder(int uid)1136         public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder(int uid) {
1137             UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid);
1138             if (builder == null) {
1139                 final BatteryConsumer.BatteryConsumerData data =
1140                         BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
1141                                 mBatteryConsumerDataLayout);
1142                 builder = new UidBatteryConsumer.Builder(data, uid, mMinConsumedPowerThreshold);
1143                 mUidBatteryConsumerBuilders.put(uid, builder);
1144             }
1145             return builder;
1146         }
1147 
1148         /**
1149          * Creates or returns a UserBatteryConsumer, which represents battery attribution
1150          * data for an individual {@link UserHandle}.
1151          */
1152         @NonNull
getOrCreateUserBatteryConsumerBuilder(int userId)1153         public UserBatteryConsumer.Builder getOrCreateUserBatteryConsumerBuilder(int userId) {
1154             UserBatteryConsumer.Builder builder = mUserBatteryConsumerBuilders.get(userId);
1155             if (builder == null) {
1156                 final BatteryConsumer.BatteryConsumerData data =
1157                         BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
1158                                 mBatteryConsumerDataLayout);
1159                 builder = new UserBatteryConsumer.Builder(data, userId, mMinConsumedPowerThreshold);
1160                 mUserBatteryConsumerBuilders.put(userId, builder);
1161             }
1162             return builder;
1163         }
1164 
1165         @NonNull
getUidBatteryConsumerBuilders()1166         public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() {
1167             return mUidBatteryConsumerBuilders;
1168         }
1169 
1170         /**
1171          * Adds battery usage stats from another snapshots. The two snapshots are assumed to be
1172          * non-overlapping, meaning that the power consumption estimates and session durations
1173          * can be simply summed across the two snapshots.  This remains true even if the timestamps
1174          * seem to indicate that the sessions are in fact overlapping: timestamps may be off as a
1175          * result of realtime clock adjustments by the user or the system.
1176          */
1177         @NonNull
add(BatteryUsageStats stats)1178         public Builder add(BatteryUsageStats stats) {
1179             if (!Arrays.equals(mCustomPowerComponentNames, stats.mCustomPowerComponentNames)) {
1180                 throw new IllegalArgumentException(
1181                         "BatteryUsageStats have different custom power components");
1182             }
1183 
1184             if (mIncludesProcessStateData && !stats.mIncludesProcessStateData) {
1185                 throw new IllegalArgumentException(
1186                         "Added BatteryUsageStats does not include process state data");
1187             }
1188 
1189             if (mUserBatteryConsumerBuilders.size() != 0
1190                     || !stats.getUserBatteryConsumers().isEmpty()) {
1191                 throw new UnsupportedOperationException(
1192                         "Combining UserBatteryConsumers is not supported");
1193             }
1194 
1195             mDischargedPowerLowerBoundMah += stats.mDischargedPowerLowerBound;
1196             mDischargedPowerUpperBoundMah += stats.mDischargedPowerUpperBound;
1197             mDischargePercentage += stats.mDischargePercentage;
1198             mDischargeDurationMs += stats.mDischargeDurationMs;
1199 
1200             mStatsDurationMs = getStatsDuration() + stats.getStatsDuration();
1201 
1202             if (mStatsStartTimestampMs == 0
1203                     || stats.mStatsStartTimestampMs < mStatsStartTimestampMs) {
1204                 mStatsStartTimestampMs = stats.mStatsStartTimestampMs;
1205             }
1206 
1207             final boolean addingLaterSnapshot = stats.mStatsEndTimestampMs > mStatsEndTimestampMs;
1208             if (addingLaterSnapshot) {
1209                 mStatsEndTimestampMs = stats.mStatsEndTimestampMs;
1210             }
1211 
1212             for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) {
1213                 getAggregateBatteryConsumerBuilder(scope)
1214                         .add(stats.mAggregateBatteryConsumers[scope]);
1215             }
1216 
1217             for (UidBatteryConsumer consumer : stats.getUidBatteryConsumers()) {
1218                 getOrCreateUidBatteryConsumerBuilder(consumer.getUid()).add(consumer);
1219             }
1220 
1221             if (addingLaterSnapshot) {
1222                 mBatteryCapacityMah = stats.mBatteryCapacityMah;
1223                 mBatteryTimeRemainingMs = stats.mBatteryTimeRemainingMs;
1224                 mChargeTimeRemainingMs = stats.mChargeTimeRemainingMs;
1225             }
1226 
1227             return this;
1228         }
1229 
1230         /**
1231          * Dumps raw contents of the cursor window for debugging.
1232          */
dump(PrintWriter writer)1233         void dump(PrintWriter writer) {
1234             final int numRows = mBatteryConsumersCursorWindow.getNumRows();
1235             int numColumns = mBatteryConsumerDataLayout.columnCount;
1236             for (int i = 0; i < numRows; i++) {
1237                 StringBuilder sb = new StringBuilder();
1238                 for (int j = 0; j < numColumns; j++) {
1239                     final int type = mBatteryConsumersCursorWindow.getType(i, j);
1240                     switch (type) {
1241                         case Cursor.FIELD_TYPE_NULL:
1242                             sb.append("null, ");
1243                             break;
1244                         case Cursor.FIELD_TYPE_INTEGER:
1245                             sb.append(mBatteryConsumersCursorWindow.getInt(i, j)).append(", ");
1246                             break;
1247                         case Cursor.FIELD_TYPE_FLOAT:
1248                             sb.append(mBatteryConsumersCursorWindow.getFloat(i, j)).append(", ");
1249                             break;
1250                         case Cursor.FIELD_TYPE_STRING:
1251                             sb.append(mBatteryConsumersCursorWindow.getString(i, j)).append(", ");
1252                             break;
1253                         case Cursor.FIELD_TYPE_BLOB:
1254                             sb.append("BLOB, ");
1255                             break;
1256                     }
1257                 }
1258                 sb.setLength(sb.length() - 2);
1259                 writer.println(sb);
1260             }
1261         }
1262     }
1263 
1264     /*
1265      * Used by tests to ensure all BatteryUsageStats instances are closed.
1266      */
1267     private static volatile boolean sInstanceLeakDetectionEnabled;
1268 
1269     @GuardedBy("BatteryUsageStats.class")
1270     private static Map<CursorWindow, Exception> sInstances;
1271 
onCursorWindowAllocated(CursorWindow window)1272     private static void onCursorWindowAllocated(CursorWindow window) {
1273         if (!sInstanceLeakDetectionEnabled) {
1274             return;
1275         }
1276 
1277         synchronized (BatteryUsageStats.class) {
1278             if (sInstances == null) {
1279                 sInstances = new HashMap<>();
1280             }
1281             sInstances.put(window, new Exception());
1282         }
1283     }
1284 
onCursorWindowReleased(CursorWindow window)1285     private static void onCursorWindowReleased(CursorWindow window) {
1286         if (!sInstanceLeakDetectionEnabled) {
1287             return;
1288         }
1289 
1290         synchronized (BatteryUsageStats.class) {
1291             sInstances.remove(window);
1292         }
1293     }
1294 
1295     /**
1296      * Enables detection of leaked BatteryUsageStats instances, meaning instances that are created
1297      * but not closed during the test execution.
1298      */
1299     @VisibleForTesting
enableInstanceLeakDetection()1300     public static void enableInstanceLeakDetection() {
1301         sInstanceLeakDetectionEnabled = true;
1302         synchronized (BatteryUsageStats.class) {
1303             if (sInstances != null) {
1304                 sInstances.clear();
1305             }
1306         }
1307     }
1308 
1309     /**
1310      * Used by tests to ensure all BatteryUsageStats instances are closed.
1311      */
1312     @VisibleForTesting
assertAllInstancesClosed()1313     public static void assertAllInstancesClosed() {
1314         if (!sInstanceLeakDetectionEnabled) {
1315             throw new IllegalStateException("Instance leak detection is not enabled");
1316         }
1317 
1318         synchronized (BatteryUsageStats.class) {
1319             if (sInstances != null && !sInstances.isEmpty()) {
1320                 Exception callSite = sInstances.entrySet().iterator().next().getValue();
1321                 int count = sInstances.size();
1322                 sInstances.clear();
1323                 throw new IllegalStateException(
1324                         "Instances of BatteryUsageStats not closed: " + count, callSite);
1325             }
1326         }
1327     }
1328 }
1329