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