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