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