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