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 the names of custom power components in order, so the first name in the array
275      * corresponds to the custom componentId
276      * {@link BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID}.
277      */
278     @NonNull
getCustomPowerComponentNames()279     public String[] getCustomPowerComponentNames() {
280         return mCustomPowerComponentNames;
281     }
282 
283     /**
284      * Returns an iterator for {@link android.os.BatteryStats.HistoryItem}'s.
285      */
286     @NonNull
iterateBatteryStatsHistory()287     public BatteryStatsHistoryIterator iterateBatteryStatsHistory() {
288         if (mHistoryBuffer == null) {
289             throw new IllegalStateException(
290                     "Battery history was not requested in the BatteryUsageStatsQuery");
291         }
292         return new BatteryStatsHistoryIterator(new BatteryStatsHistory(mHistoryBuffer),
293                 mHistoryTagPool);
294     }
295 
296     @Override
describeContents()297     public int describeContents() {
298         return 0;
299     }
300 
BatteryUsageStats(@onNull Parcel source)301     private BatteryUsageStats(@NonNull Parcel source) {
302         mStatsStartTimestampMs = source.readLong();
303         mStatsEndTimestampMs = source.readLong();
304         mStatsDurationMs = source.readLong();
305         mBatteryCapacityMah = source.readDouble();
306         mDischargePercentage = source.readInt();
307         mDischargedPowerLowerBound = source.readDouble();
308         mDischargedPowerUpperBound = source.readDouble();
309         mBatteryTimeRemainingMs = source.readLong();
310         mChargeTimeRemainingMs = source.readLong();
311         mCustomPowerComponentNames = source.readStringArray();
312         mAggregateBatteryConsumers =
313                 new AggregateBatteryConsumer[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
314         for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) {
315             mAggregateBatteryConsumers[i] =
316                     AggregateBatteryConsumer.CREATOR.createFromParcel(source);
317             mAggregateBatteryConsumers[i].setCustomPowerComponentNames(mCustomPowerComponentNames);
318         }
319 
320         // UidBatteryConsumers are included as a blob to avoid a TransactionTooLargeException
321         final Parcel blob = Parcel.obtain();
322         final byte[] bytes = source.readBlob();
323         blob.unmarshall(bytes, 0, bytes.length);
324         blob.setDataPosition(0);
325 
326         final int uidCount = blob.readInt();
327         mUidBatteryConsumers = new ArrayList<>(uidCount);
328         for (int i = 0; i < uidCount; i++) {
329             final UidBatteryConsumer consumer =
330                     UidBatteryConsumer.CREATOR.createFromParcel(blob);
331             consumer.setCustomPowerComponentNames(mCustomPowerComponentNames);
332             mUidBatteryConsumers.add(consumer);
333         }
334         blob.recycle();
335 
336         int userCount = source.readInt();
337         mUserBatteryConsumers = new ArrayList<>(userCount);
338         for (int i = 0; i < userCount; i++) {
339             final UserBatteryConsumer consumer =
340                     UserBatteryConsumer.CREATOR.createFromParcel(source);
341             consumer.setCustomPowerComponentNames(mCustomPowerComponentNames);
342             mUserBatteryConsumers.add(consumer);
343         }
344         if (source.readBoolean()) {
345             final byte[] historyBlob = source.readBlob();
346 
347             mHistoryBuffer = Parcel.obtain();
348             mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length);
349 
350             int historyTagCount = source.readInt();
351             mHistoryTagPool = new ArrayList<>(historyTagCount);
352             for (int i = 0; i < historyTagCount; i++) {
353                 BatteryStats.HistoryTag tag = new BatteryStats.HistoryTag();
354                 tag.string = source.readString();
355                 tag.uid = source.readInt();
356                 tag.poolIdx = source.readInt();
357                 mHistoryTagPool.add(tag);
358             }
359         } else {
360             mHistoryBuffer = null;
361             mHistoryTagPool = null;
362         }
363     }
364 
365     @Override
writeToParcel(@onNull Parcel dest, int flags)366     public void writeToParcel(@NonNull Parcel dest, int flags) {
367         dest.writeLong(mStatsStartTimestampMs);
368         dest.writeLong(mStatsEndTimestampMs);
369         dest.writeLong(mStatsDurationMs);
370         dest.writeDouble(mBatteryCapacityMah);
371         dest.writeInt(mDischargePercentage);
372         dest.writeDouble(mDischargedPowerLowerBound);
373         dest.writeDouble(mDischargedPowerUpperBound);
374         dest.writeLong(mBatteryTimeRemainingMs);
375         dest.writeLong(mChargeTimeRemainingMs);
376         dest.writeStringArray(mCustomPowerComponentNames);
377         for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) {
378             mAggregateBatteryConsumers[i].writeToParcel(dest, flags);
379         }
380 
381         // UidBatteryConsumers are included as a blob, because each UidBatteryConsumer
382         // takes > 300 bytes, so a typical number of UIDs in the system, 300 would result
383         // in a 90 kB Parcel, which is not safe to pass in a binder transaction because
384         // of the possibility of TransactionTooLargeException
385         final Parcel blob = Parcel.obtain();
386         blob.writeInt(mUidBatteryConsumers.size());
387         for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) {
388             mUidBatteryConsumers.get(i).writeToParcel(blob, flags);
389         }
390         dest.writeBlob(blob.marshall());
391         blob.recycle();
392 
393         dest.writeInt(mUserBatteryConsumers.size());
394         for (int i = mUserBatteryConsumers.size() - 1; i >= 0; i--) {
395             mUserBatteryConsumers.get(i).writeToParcel(dest, flags);
396         }
397         if (mHistoryBuffer != null) {
398             dest.writeBoolean(true);
399             dest.writeBlob(mHistoryBuffer.marshall());
400             dest.writeInt(mHistoryTagPool.size());
401             for (int i = mHistoryTagPool.size() - 1; i >= 0; i--) {
402                 final BatteryStats.HistoryTag tag = mHistoryTagPool.get(i);
403                 dest.writeString(tag.string);
404                 dest.writeInt(tag.uid);
405                 dest.writeInt(tag.poolIdx);
406             }
407         } else {
408             dest.writeBoolean(false);
409         }
410     }
411 
412     @NonNull
413     public static final Creator<BatteryUsageStats> CREATOR = new Creator<BatteryUsageStats>() {
414         public BatteryUsageStats createFromParcel(@NonNull Parcel source) {
415             return new BatteryUsageStats(source);
416         }
417 
418         public BatteryUsageStats[] newArray(int size) {
419             return new BatteryUsageStats[size];
420         }
421     };
422 
423     /** Returns a proto (as used for atoms.proto) corresponding to this BatteryUsageStats. */
getStatsProto()424     public byte[] getStatsProto() {
425         // ProtoOutputStream.getRawSize() returns the buffer size before compaction.
426         // BatteryUsageStats contains a lot of integers, so compaction of integers to
427         // varint reduces the size of the proto buffer by as much as 50%.
428         int maxRawSize = (int) (STATSD_PULL_ATOM_MAX_BYTES * 1.75);
429         // Limit the number of attempts in order to prevent an infinite loop
430         for (int i = 0; i < 3; i++) {
431             final ProtoOutputStream proto = new ProtoOutputStream();
432             writeStatsProto(proto, maxRawSize);
433 
434             final int rawSize = proto.getRawSize();
435             final byte[] protoOutput = proto.getBytes();
436 
437             if (protoOutput.length <= STATSD_PULL_ATOM_MAX_BYTES) {
438                 return protoOutput;
439             }
440 
441             // Adjust maxRawSize proportionately and try again.
442             maxRawSize =
443                     (int) ((long) STATSD_PULL_ATOM_MAX_BYTES * rawSize / protoOutput.length - 1024);
444         }
445 
446         // Fallback: if we have failed to generate a proto smaller than STATSD_PULL_ATOM_MAX_BYTES,
447         // just generate a proto with the _rawSize_ of STATSD_PULL_ATOM_MAX_BYTES, which is
448         // guaranteed to produce a compacted proto (significantly) smaller than
449         // STATSD_PULL_ATOM_MAX_BYTES.
450         final ProtoOutputStream proto = new ProtoOutputStream();
451         writeStatsProto(proto, STATSD_PULL_ATOM_MAX_BYTES);
452         return proto.getBytes();
453     }
454 
455     @NonNull
writeStatsProto(ProtoOutputStream proto, int maxRawSize)456     private void writeStatsProto(ProtoOutputStream proto, int maxRawSize) {
457         final BatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer(
458                 AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
459 
460         proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, getStatsStartTimestamp());
461         proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, getStatsEndTimestamp());
462         proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, getStatsDuration());
463         proto.write(BatteryUsageStatsAtomsProto.SESSION_DISCHARGE_PERCENTAGE,
464                 getDischargePercentage());
465         deviceBatteryConsumer.writeStatsProto(proto,
466                 BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER);
467         writeUidBatteryConsumersProto(proto, maxRawSize);
468     }
469 
470     /**
471      * Writes the UidBatteryConsumers data, held by this BatteryUsageStats, to the proto (as used
472      * for atoms.proto).
473      */
writeUidBatteryConsumersProto(ProtoOutputStream proto, int maxRawSize)474     private void writeUidBatteryConsumersProto(ProtoOutputStream proto, int maxRawSize) {
475         final List<UidBatteryConsumer> consumers = getUidBatteryConsumers();
476         // Order consumers by descending weight (a combination of consumed power and usage time)
477         consumers.sort(Comparator.comparingDouble(this::getUidBatteryConsumerWeight).reversed());
478 
479         final int size = consumers.size();
480         for (int i = 0; i < size; i++) {
481             final UidBatteryConsumer consumer = consumers.get(i);
482 
483             final long fgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
484             final long bgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
485             final boolean hasBaseData = consumer.hasStatsProtoData();
486 
487             if (fgMs == 0 && bgMs == 0 && !hasBaseData) {
488                 continue;
489             }
490 
491             final long token = proto.start(BatteryUsageStatsAtomsProto.UID_BATTERY_CONSUMERS);
492             proto.write(
493                     BatteryUsageStatsAtomsProto.UidBatteryConsumer.UID,
494                     consumer.getUid());
495             if (hasBaseData) {
496                 consumer.writeStatsProto(proto,
497                         BatteryUsageStatsAtomsProto.UidBatteryConsumer.BATTERY_CONSUMER_DATA);
498             }
499             proto.write(
500                     BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_FOREGROUND_MILLIS,
501                     fgMs);
502             proto.write(
503                     BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_BACKGROUND_MILLIS,
504                     bgMs);
505             proto.end(token);
506 
507             if (proto.getRawSize() >= maxRawSize) {
508                 break;
509             }
510         }
511     }
512 
513     private static final double WEIGHT_CONSUMED_POWER = 1;
514     // Weight one hour in foreground the same as 100 mAh of power drain
515     private static final double WEIGHT_FOREGROUND_STATE = 100.0 / (1 * 60 * 60 * 1000);
516     // Weight one hour in background the same as 300 mAh of power drain
517     private static final double WEIGHT_BACKGROUND_STATE = 300.0 / (1 * 60 * 60 * 1000);
518 
519     /**
520      * Computes the weight associated with a UidBatteryConsumer, which is used for sorting.
521      * We want applications with the largest consumed power as well as applications
522      * with the highest usage time to be included in the statsd atom.
523      */
getUidBatteryConsumerWeight(UidBatteryConsumer uidBatteryConsumer)524     private double getUidBatteryConsumerWeight(UidBatteryConsumer uidBatteryConsumer) {
525         final double consumedPower = uidBatteryConsumer.getConsumedPower();
526         final long timeInForeground =
527                 uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
528         final long timeInBackground =
529                 uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
530         return consumedPower * WEIGHT_CONSUMED_POWER
531                 + timeInForeground * WEIGHT_FOREGROUND_STATE
532                 + timeInBackground * WEIGHT_BACKGROUND_STATE;
533     }
534 
535     /**
536      * Prints the stats in a human-readable format.
537      */
dump(PrintWriter pw, String prefix)538     public void dump(PrintWriter pw, String prefix) {
539         pw.print(prefix);
540         pw.println("  Estimated power use (mAh):");
541         pw.print(prefix);
542         pw.print("    Capacity: ");
543         PowerCalculator.printPowerMah(pw, getBatteryCapacity());
544         pw.print(", Computed drain: ");
545         PowerCalculator.printPowerMah(pw, getConsumedPower());
546         final Range<Double> dischargedPowerRange = getDischargedPowerRange();
547         pw.print(", actual drain: ");
548         PowerCalculator.printPowerMah(pw, dischargedPowerRange.getLower());
549         if (!dischargedPowerRange.getLower().equals(dischargedPowerRange.getUpper())) {
550             pw.print("-");
551             PowerCalculator.printPowerMah(pw, dischargedPowerRange.getUpper());
552         }
553         pw.println();
554 
555         pw.println("    Global");
556         final BatteryConsumer deviceConsumer = getAggregateBatteryConsumer(
557                 AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
558         final BatteryConsumer appsConsumer = getAggregateBatteryConsumer(
559                 AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
560 
561         for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
562                 componentId++) {
563             final double devicePowerMah = deviceConsumer.getConsumedPower(componentId);
564             final double appsPowerMah = appsConsumer.getConsumedPower(componentId);
565             if (devicePowerMah == 0 && appsPowerMah == 0) {
566                 continue;
567             }
568 
569             final String componentName = BatteryConsumer.powerComponentIdToString(componentId);
570             printPowerComponent(pw, prefix, componentName, devicePowerMah, appsPowerMah,
571                     deviceConsumer.getPowerModel(componentId),
572                     deviceConsumer.getUsageDurationMillis(componentId));
573         }
574 
575         for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
576                 componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
577                         + mCustomPowerComponentNames.length;
578                 componentId++) {
579             final double devicePowerMah =
580                     deviceConsumer.getConsumedPowerForCustomComponent(componentId);
581             final double appsPowerMah =
582                     appsConsumer.getConsumedPowerForCustomComponent(componentId);
583             if (devicePowerMah == 0 && appsPowerMah == 0) {
584                 continue;
585             }
586 
587             printPowerComponent(pw, prefix, deviceConsumer.getCustomPowerComponentName(componentId),
588                     devicePowerMah, appsPowerMah,
589                     BatteryConsumer.POWER_MODEL_UNDEFINED,
590                     deviceConsumer.getUsageDurationForCustomComponentMillis(componentId));
591         }
592 
593         dumpSortedBatteryConsumers(pw, prefix, getUidBatteryConsumers());
594         dumpSortedBatteryConsumers(pw, prefix, getUserBatteryConsumers());
595     }
596 
printPowerComponent(PrintWriter pw, String prefix, String componentName, double devicePowerMah, double appsPowerMah, int powerModel, long durationMs)597     private void printPowerComponent(PrintWriter pw, String prefix, String componentName,
598             double devicePowerMah, double appsPowerMah, int powerModel, long durationMs) {
599         StringBuilder sb = new StringBuilder();
600         sb.append(prefix).append("      ").append(componentName).append(": ")
601                 .append(PowerCalculator.formatCharge(devicePowerMah));
602         if (powerModel != BatteryConsumer.POWER_MODEL_UNDEFINED
603                 && powerModel != BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
604             sb.append(" [");
605             sb.append(BatteryConsumer.powerModelToString(powerModel));
606             sb.append("]");
607         }
608         sb.append(" apps: ").append(PowerCalculator.formatCharge(appsPowerMah));
609         if (durationMs != 0) {
610             sb.append(" duration: ");
611             BatteryStats.formatTimeMs(sb, durationMs);
612         }
613 
614         pw.println(sb.toString());
615     }
616 
dumpSortedBatteryConsumers(PrintWriter pw, String prefix, List<? extends BatteryConsumer> batteryConsumers)617     private void dumpSortedBatteryConsumers(PrintWriter pw, String prefix,
618             List<? extends BatteryConsumer> batteryConsumers) {
619         batteryConsumers.sort(
620                 Comparator.<BatteryConsumer>comparingDouble(BatteryConsumer::getConsumedPower)
621                         .reversed());
622         for (BatteryConsumer consumer : batteryConsumers) {
623             if (consumer.getConsumedPower() == 0) {
624                 continue;
625             }
626             pw.print(prefix);
627             pw.print("    ");
628             consumer.dump(pw);
629             pw.println();
630         }
631     }
632 
633     /** Serializes this object to XML */
writeXml(TypedXmlSerializer serializer)634     public void writeXml(TypedXmlSerializer serializer) throws IOException {
635         serializer.startTag(null, XML_TAG_BATTERY_USAGE_STATS);
636 
637         for (int i = 0; i < mCustomPowerComponentNames.length; i++) {
638             serializer.attribute(null, XML_ATTR_PREFIX_CUSTOM_COMPONENT + i,
639                     mCustomPowerComponentNames[i]);
640         }
641 
642         serializer.attributeLong(null, XML_ATTR_START_TIMESTAMP, mStatsStartTimestampMs);
643         serializer.attributeLong(null, XML_ATTR_END_TIMESTAMP, mStatsEndTimestampMs);
644         serializer.attributeLong(null, XML_ATTR_DURATION, mStatsDurationMs);
645         serializer.attributeDouble(null, XML_ATTR_BATTERY_CAPACITY, mBatteryCapacityMah);
646         serializer.attributeInt(null, XML_ATTR_DISCHARGE_PERCENT, mDischargePercentage);
647         serializer.attributeDouble(null, XML_ATTR_DISCHARGE_LOWER, mDischargedPowerLowerBound);
648         serializer.attributeDouble(null, XML_ATTR_DISCHARGE_UPPER, mDischargedPowerUpperBound);
649         serializer.attributeLong(null, XML_ATTR_BATTERY_REMAINING, mBatteryTimeRemainingMs);
650         serializer.attributeLong(null, XML_ATTR_CHARGE_REMAINING, mChargeTimeRemainingMs);
651 
652         for (int scope = 0; scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
653                 scope++) {
654             mAggregateBatteryConsumers[scope].writeToXml(serializer, scope);
655         }
656         for (UidBatteryConsumer consumer : mUidBatteryConsumers) {
657             consumer.writeToXml(serializer);
658         }
659         for (UserBatteryConsumer consumer : mUserBatteryConsumers) {
660             consumer.writeToXml(serializer);
661         }
662         serializer.endTag(null, XML_TAG_BATTERY_USAGE_STATS);
663     }
664 
665     /** Parses an XML representation of BatteryUsageStats */
createFromXml(TypedXmlPullParser parser)666     public static BatteryUsageStats createFromXml(TypedXmlPullParser parser)
667             throws XmlPullParserException, IOException {
668         Builder builder = null;
669         int eventType = parser.getEventType();
670         while (eventType != XmlPullParser.END_DOCUMENT) {
671             if (eventType == XmlPullParser.START_TAG
672                     && parser.getName().equals(XML_TAG_BATTERY_USAGE_STATS)) {
673                 List<String> customComponentNames = new ArrayList<>();
674                 int i = 0;
675                 while (true) {
676                     int index = parser.getAttributeIndex(null,
677                             XML_ATTR_PREFIX_CUSTOM_COMPONENT + i);
678                     if (index == -1) {
679                         break;
680                     }
681                     customComponentNames.add(parser.getAttributeValue(index));
682                     i++;
683                 }
684 
685                 builder = new Builder(
686                         customComponentNames.toArray(new String[0]), true);
687 
688                 builder.setStatsStartTimestamp(
689                         parser.getAttributeLong(null, XML_ATTR_START_TIMESTAMP));
690                 builder.setStatsEndTimestamp(
691                         parser.getAttributeLong(null, XML_ATTR_END_TIMESTAMP));
692                 builder.setStatsDuration(
693                         parser.getAttributeLong(null, XML_ATTR_DURATION));
694                 builder.setBatteryCapacity(
695                         parser.getAttributeDouble(null, XML_ATTR_BATTERY_CAPACITY));
696                 builder.setDischargePercentage(
697                         parser.getAttributeInt(null, XML_ATTR_DISCHARGE_PERCENT));
698                 builder.setDischargedPowerRange(
699                         parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_LOWER),
700                         parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_UPPER));
701                 builder.setBatteryTimeRemainingMs(
702                         parser.getAttributeLong(null, XML_ATTR_BATTERY_REMAINING));
703                 builder.setChargeTimeRemainingMs(
704                         parser.getAttributeLong(null, XML_ATTR_CHARGE_REMAINING));
705 
706                 eventType = parser.next();
707                 break;
708             }
709             eventType = parser.next();
710         }
711 
712         if (builder == null) {
713             throw new XmlPullParserException("No root element");
714         }
715 
716         while (eventType != XmlPullParser.END_DOCUMENT) {
717             if (eventType == XmlPullParser.START_TAG) {
718                 switch (parser.getName()) {
719                     case XML_TAG_AGGREGATE:
720                         AggregateBatteryConsumer.parseXml(parser, builder);
721                         break;
722                     case XML_TAG_UID:
723                         UidBatteryConsumer.createFromXml(parser, builder);
724                         break;
725                     case XML_TAG_USER:
726                         UserBatteryConsumer.createFromXml(parser, builder);
727                         break;
728                 }
729             }
730             eventType = parser.next();
731         }
732 
733         return builder.build();
734     }
735 
736     /**
737      * Builder for BatteryUsageStats.
738      */
739     public static final class Builder {
740         @NonNull
741         private final String[] mCustomPowerComponentNames;
742         private final boolean mIncludePowerModels;
743         private long mStatsStartTimestampMs;
744         private long mStatsEndTimestampMs;
745         private long mStatsDurationMs = -1;
746         private double mBatteryCapacityMah;
747         private int mDischargePercentage;
748         private double mDischargedPowerLowerBoundMah;
749         private double mDischargedPowerUpperBoundMah;
750         private long mBatteryTimeRemainingMs = -1;
751         private long mChargeTimeRemainingMs = -1;
752         private final AggregateBatteryConsumer.Builder[] mAggregateBatteryConsumersBuilders =
753                 new AggregateBatteryConsumer.Builder[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
754         private final SparseArray<UidBatteryConsumer.Builder> mUidBatteryConsumerBuilders =
755                 new SparseArray<>();
756         private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders =
757                 new SparseArray<>();
758         private Parcel mHistoryBuffer;
759         private List<BatteryStats.HistoryTag> mHistoryTagPool;
760 
Builder(@onNull String[] customPowerComponentNames)761         public Builder(@NonNull String[] customPowerComponentNames) {
762             this(customPowerComponentNames, false);
763         }
764 
Builder(@onNull String[] customPowerComponentNames, boolean includePowerModels)765         public Builder(@NonNull String[] customPowerComponentNames,  boolean includePowerModels) {
766             mCustomPowerComponentNames = customPowerComponentNames;
767             mIncludePowerModels = includePowerModels;
768             for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) {
769                 mAggregateBatteryConsumersBuilders[i] = new AggregateBatteryConsumer.Builder(
770                         customPowerComponentNames, includePowerModels);
771             }
772         }
773 
774         /**
775          * Constructs a read-only object using the Builder values.
776          */
777         @NonNull
build()778         public BatteryUsageStats build() {
779             return new BatteryUsageStats(this);
780         }
781 
782         /**
783          * Sets the battery capacity in milli-amp-hours.
784          */
setBatteryCapacity(double batteryCapacityMah)785         public Builder setBatteryCapacity(double batteryCapacityMah) {
786             mBatteryCapacityMah = batteryCapacityMah;
787             return this;
788         }
789 
790         /**
791          * Sets the timestamp of the latest battery stats reset, in milliseconds.
792          */
setStatsStartTimestamp(long statsStartTimestampMs)793         public Builder setStatsStartTimestamp(long statsStartTimestampMs) {
794             mStatsStartTimestampMs = statsStartTimestampMs;
795             return this;
796         }
797 
798         /**
799          * Sets the timestamp of when the battery stats snapshot was taken, in milliseconds.
800          */
setStatsEndTimestamp(long statsEndTimestampMs)801         public Builder setStatsEndTimestamp(long statsEndTimestampMs) {
802             mStatsEndTimestampMs = statsEndTimestampMs;
803             return this;
804         }
805 
806         /**
807          * Sets the duration of the stats session.  The default value of this field is
808          * statsEndTimestamp - statsStartTimestamp.
809          */
setStatsDuration(long statsDurationMs)810         public Builder setStatsDuration(long statsDurationMs) {
811             mStatsDurationMs = statsDurationMs;
812             return this;
813         }
814 
getStatsDuration()815         private long getStatsDuration() {
816             if (mStatsDurationMs != -1) {
817                 return mStatsDurationMs;
818             } else {
819                 return mStatsEndTimestampMs - mStatsStartTimestampMs;
820             }
821         }
822 
823         /**
824          * Sets the battery discharge amount since BatteryStats reset as percentage of the full
825          * charge.
826          */
827         @NonNull
setDischargePercentage(int dischargePercentage)828         public Builder setDischargePercentage(int dischargePercentage) {
829             mDischargePercentage = dischargePercentage;
830             return this;
831         }
832 
833         /**
834          * Sets the estimated battery discharge range.
835          */
836         @NonNull
setDischargedPowerRange(double dischargedPowerLowerBoundMah, double dischargedPowerUpperBoundMah)837         public Builder setDischargedPowerRange(double dischargedPowerLowerBoundMah,
838                 double dischargedPowerUpperBoundMah) {
839             mDischargedPowerLowerBoundMah = dischargedPowerLowerBoundMah;
840             mDischargedPowerUpperBoundMah = dischargedPowerUpperBoundMah;
841             return this;
842         }
843 
844         /**
845          * Sets an approximation for how much time (in milliseconds) remains until the battery
846          * is fully discharged.
847          */
848         @NonNull
setBatteryTimeRemainingMs(long batteryTimeRemainingMs)849         public Builder setBatteryTimeRemainingMs(long batteryTimeRemainingMs) {
850             mBatteryTimeRemainingMs = batteryTimeRemainingMs;
851             return this;
852         }
853 
854         /**
855          * Sets an approximation for how much time (in milliseconds) remains until the battery
856          * is fully charged.
857          */
858         @NonNull
setChargeTimeRemainingMs(long chargeTimeRemainingMs)859         public Builder setChargeTimeRemainingMs(long chargeTimeRemainingMs) {
860             mChargeTimeRemainingMs = chargeTimeRemainingMs;
861             return this;
862         }
863 
864         /**
865          * Sets the parceled recent history.
866          */
867         @NonNull
setBatteryHistory(Parcel historyBuffer, List<BatteryStats.HistoryTag> historyTagPool)868         public Builder setBatteryHistory(Parcel historyBuffer,
869                 List<BatteryStats.HistoryTag> historyTagPool) {
870             mHistoryBuffer = historyBuffer;
871             mHistoryTagPool = historyTagPool;
872             return this;
873         }
874 
875         /**
876          * Creates or returns an AggregateBatteryConsumer builder, which represents aggregate
877          * battery consumption data for the specified scope.
878          */
879         @NonNull
getAggregateBatteryConsumerBuilder( @ggregateBatteryConsumerScope int scope)880         public AggregateBatteryConsumer.Builder getAggregateBatteryConsumerBuilder(
881                 @AggregateBatteryConsumerScope int scope) {
882             return mAggregateBatteryConsumersBuilders[scope];
883         }
884 
885         /**
886          * Creates or returns a UidBatteryConsumer, which represents battery attribution
887          * data for an individual UID.
888          */
889         @NonNull
getOrCreateUidBatteryConsumerBuilder( @onNull BatteryStats.Uid batteryStatsUid)890         public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder(
891                 @NonNull BatteryStats.Uid batteryStatsUid) {
892             int uid = batteryStatsUid.getUid();
893             UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid);
894             if (builder == null) {
895                 builder = new UidBatteryConsumer.Builder(mCustomPowerComponentNames,
896                         mIncludePowerModels, batteryStatsUid);
897                 mUidBatteryConsumerBuilders.put(uid, builder);
898             }
899             return builder;
900         }
901 
902         /**
903          * Creates or returns a UidBatteryConsumer, which represents battery attribution
904          * data for an individual UID. This version of the method is not suitable for use
905          * with PowerCalculators.
906          */
907         @NonNull
getOrCreateUidBatteryConsumerBuilder(int uid)908         public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder(int uid) {
909             UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid);
910             if (builder == null) {
911                 builder = new UidBatteryConsumer.Builder(mCustomPowerComponentNames,
912                         mIncludePowerModels, uid);
913                 mUidBatteryConsumerBuilders.put(uid, builder);
914             }
915             return builder;
916         }
917 
918         /**
919          * Creates or returns a UserBatteryConsumer, which represents battery attribution
920          * data for an individual {@link UserHandle}.
921          */
922         @NonNull
getOrCreateUserBatteryConsumerBuilder(int userId)923         public UserBatteryConsumer.Builder getOrCreateUserBatteryConsumerBuilder(int userId) {
924             UserBatteryConsumer.Builder builder = mUserBatteryConsumerBuilders.get(userId);
925             if (builder == null) {
926                 builder = new UserBatteryConsumer.Builder(mCustomPowerComponentNames,
927                         mIncludePowerModels, userId);
928                 mUserBatteryConsumerBuilders.put(userId, builder);
929             }
930             return builder;
931         }
932 
933         @NonNull
getUidBatteryConsumerBuilders()934         public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() {
935             return mUidBatteryConsumerBuilders;
936         }
937 
938         /**
939          * Adds battery usage stats from another snapshots. The two snapshots are assumed to be
940          * non-overlapping, meaning that the power consumption estimates and session durations
941          * can be simply summed across the two snapshots.  This remains true even if the timestamps
942          * seem to indicate that the sessions are in fact overlapping: timestamps may be off as a
943          * result of realtime clock adjustments by the user or the system.
944          */
945         @NonNull
add(BatteryUsageStats stats)946         public Builder add(BatteryUsageStats stats) {
947             if (!Arrays.equals(mCustomPowerComponentNames, stats.mCustomPowerComponentNames)) {
948                 throw new IllegalArgumentException(
949                         "BatteryUsageStats have different custom power components");
950             }
951 
952             if (mUserBatteryConsumerBuilders.size() != 0
953                     || !stats.getUserBatteryConsumers().isEmpty()) {
954                 throw new UnsupportedOperationException(
955                         "Combining UserBatteryConsumers is not supported");
956             }
957 
958             mDischargedPowerLowerBoundMah += stats.mDischargedPowerLowerBound;
959             mDischargedPowerUpperBoundMah += stats.mDischargedPowerUpperBound;
960             mDischargePercentage += stats.mDischargePercentage;
961 
962             mStatsDurationMs = getStatsDuration() + stats.getStatsDuration();
963 
964             if (mStatsStartTimestampMs == 0
965                     || stats.mStatsStartTimestampMs < mStatsStartTimestampMs) {
966                 mStatsStartTimestampMs = stats.mStatsStartTimestampMs;
967             }
968 
969             final boolean addingLaterSnapshot = stats.mStatsEndTimestampMs > mStatsEndTimestampMs;
970             if (addingLaterSnapshot) {
971                 mStatsEndTimestampMs = stats.mStatsEndTimestampMs;
972             }
973 
974             for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) {
975                 getAggregateBatteryConsumerBuilder(scope)
976                         .add(stats.mAggregateBatteryConsumers[scope]);
977             }
978 
979             for (UidBatteryConsumer consumer : stats.getUidBatteryConsumers()) {
980                 getOrCreateUidBatteryConsumerBuilder(consumer.getUid()).add(consumer);
981             }
982 
983             if (addingLaterSnapshot) {
984                 mBatteryCapacityMah = stats.mBatteryCapacityMah;
985                 mBatteryTimeRemainingMs = stats.mBatteryTimeRemainingMs;
986                 mChargeTimeRemainingMs = stats.mChargeTimeRemainingMs;
987             }
988 
989             return this;
990         }
991     }
992 }
993