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