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