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