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.annotations.GuardedBy; 28 import com.android.internal.annotations.VisibleForTesting; 29 import com.android.internal.os.BatteryStatsHistory; 30 import com.android.internal.os.BatteryStatsHistoryIterator; 31 import com.android.internal.os.MonotonicClock; 32 import com.android.modules.utils.TypedXmlPullParser; 33 import com.android.modules.utils.TypedXmlSerializer; 34 35 import org.xmlpull.v1.XmlPullParser; 36 import org.xmlpull.v1.XmlPullParserException; 37 38 import java.io.Closeable; 39 import java.io.FileDescriptor; 40 import java.io.IOException; 41 import java.io.PrintWriter; 42 import java.io.StringWriter; 43 import java.lang.annotation.Retention; 44 import java.lang.annotation.RetentionPolicy; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Comparator; 48 import java.util.HashMap; 49 import java.util.List; 50 import java.util.Map; 51 52 /** 53 * Contains a snapshot of battery attribution data, on a per-subsystem and per-UID basis. 54 * <p> 55 * The totals for the entire device are returned as AggregateBatteryConsumers, which can be 56 * obtained by calling {@link #getAggregateBatteryConsumer(int)}. 57 * <p> 58 * Power attributed to individual apps is returned as UidBatteryConsumers, see 59 * {@link #getUidBatteryConsumers()}. 60 * 61 * @hide 62 */ 63 @android.ravenwood.annotation.RavenwoodKeepWholeClass 64 public final class BatteryUsageStats implements Parcelable, Closeable { 65 66 /** 67 * Scope of battery stats included in a BatteryConsumer: the entire device, just 68 * the apps, etc. 69 * 70 * @hide 71 */ 72 @IntDef(prefix = {"AGGREGATE_BATTERY_CONSUMER_SCOPE_"}, value = { 73 AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, 74 AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS, 75 }) 76 @Retention(RetentionPolicy.SOURCE) 77 public static @interface AggregateBatteryConsumerScope { 78 } 79 80 /** 81 * Power consumption by the entire device, since last charge. The power usage in this 82 * scope includes both the power attributed to apps and the power unattributed to any 83 * apps. 84 */ 85 public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE = 0; 86 87 /** 88 * Aggregated power consumed by all applications, combined, since last charge. This is 89 * the sum of power reported in UidBatteryConsumers. 90 */ 91 public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS = 1; 92 93 public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT = 2; 94 95 // XML tags and attributes for BatteryUsageStats persistence 96 static final String XML_TAG_BATTERY_USAGE_STATS = "battery_usage_stats"; 97 static final String XML_TAG_AGGREGATE = "aggregate"; 98 static final String XML_TAG_UID = "uid"; 99 static final String XML_TAG_USER = "user"; 100 static final String XML_TAG_POWER_COMPONENTS = "power_components"; 101 static final String XML_TAG_COMPONENT = "component"; 102 static final String XML_ATTR_ID = "id"; 103 static final String XML_ATTR_UID = "uid"; 104 static final String XML_ATTR_USER_ID = "user_id"; 105 static final String XML_ATTR_SCOPE = "scope"; 106 static final String XML_ATTR_PREFIX_CUSTOM_COMPONENT = "custom_component_"; 107 static final String XML_ATTR_PREFIX_INCLUDES_PROC_STATE_DATA = "includes_proc_state_data"; 108 static final String XML_ATTR_PREFIX_INCLUDES_SCREEN_STATE_DATA = "includes_screen_state_data"; 109 static final String XML_ATTR_PREFIX_INCLUDES_POWER_STATE_DATA = "includes_power_state_data"; 110 static final String XML_ATTR_START_TIMESTAMP = "start_timestamp"; 111 static final String XML_ATTR_END_TIMESTAMP = "end_timestamp"; 112 static final String XML_ATTR_PROCESS_STATE = "process_state"; 113 static final String XML_ATTR_SCREEN_STATE = "screen_state"; 114 static final String XML_ATTR_POWER_STATE = "power_state"; 115 static final String XML_ATTR_POWER = "power"; 116 static final String XML_ATTR_DURATION = "duration"; 117 static final String XML_ATTR_BATTERY_CAPACITY = "battery_capacity"; 118 static final String XML_ATTR_DISCHARGE_PERCENT = "discharge_pct"; 119 static final String XML_ATTR_DISCHARGE_LOWER = "discharge_lower"; 120 static final String XML_ATTR_DISCHARGE_UPPER = "discharge_upper"; 121 static final String XML_ATTR_DISCHARGE_DURATION = "discharge_duration"; 122 static final String XML_ATTR_BATTERY_REMAINING = "battery_remaining"; 123 static final String XML_ATTR_CHARGE_REMAINING = "charge_remaining"; 124 static final String XML_ATTR_HIGHEST_DRAIN_PACKAGE = "highest_drain_package"; 125 static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground"; 126 static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background"; 127 static final String XML_ATTR_TIME_IN_FOREGROUND_SERVICE = "time_in_foreground_service"; 128 129 // Max window size. CursorWindow uses only as much memory as needed. 130 private static final long BATTERY_CONSUMER_CURSOR_WINDOW_SIZE = 20_000_000; // bytes 131 132 private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000; 133 134 private static final int[] UID_USAGE_TIME_PROCESS_STATES = { 135 BatteryConsumer.PROCESS_STATE_FOREGROUND, 136 BatteryConsumer.PROCESS_STATE_BACKGROUND, 137 BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE 138 }; 139 140 private final int mDischargePercentage; 141 private final double mBatteryCapacityMah; 142 private final long mStatsStartTimestampMs; 143 private final long mStatsEndTimestampMs; 144 private final long mStatsDurationMs; 145 private final double mDischargedPowerLowerBound; 146 private final double mDischargedPowerUpperBound; 147 private final long mDischargeDurationMs; 148 private final long mBatteryTimeRemainingMs; 149 private final long mChargeTimeRemainingMs; 150 private final String[] mCustomPowerComponentNames; 151 private final boolean mIncludesProcessStateData; 152 private final boolean mIncludesScreenStateData; 153 private final boolean mIncludesPowerStateData; 154 private final List<UidBatteryConsumer> mUidBatteryConsumers; 155 private final List<UserBatteryConsumer> mUserBatteryConsumers; 156 private final AggregateBatteryConsumer[] mAggregateBatteryConsumers; 157 private final BatteryStatsHistory mBatteryStatsHistory; 158 private final long mPreferredHistoryDurationMs; 159 private final BatteryConsumer.BatteryConsumerDataLayout mBatteryConsumerDataLayout; 160 private CursorWindow mBatteryConsumersCursorWindow; 161 BatteryUsageStats(@onNull Builder builder)162 private BatteryUsageStats(@NonNull Builder builder) { 163 mStatsStartTimestampMs = builder.mStatsStartTimestampMs; 164 mStatsEndTimestampMs = builder.mStatsEndTimestampMs; 165 mStatsDurationMs = builder.getStatsDuration(); 166 mBatteryCapacityMah = builder.mBatteryCapacityMah; 167 mDischargePercentage = builder.mDischargePercentage; 168 mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah; 169 mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah; 170 mDischargeDurationMs = builder.mDischargeDurationMs; 171 mBatteryStatsHistory = builder.mBatteryStatsHistory; 172 mPreferredHistoryDurationMs = builder.mPreferredHistoryDurationMs; 173 mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs; 174 mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs; 175 mCustomPowerComponentNames = builder.mCustomPowerComponentNames; 176 mIncludesProcessStateData = builder.mIncludesProcessStateData; 177 mIncludesScreenStateData = builder.mIncludesScreenStateData; 178 mIncludesPowerStateData = builder.mIncludesPowerStateData; 179 mBatteryConsumerDataLayout = builder.mBatteryConsumerDataLayout; 180 mBatteryConsumersCursorWindow = builder.mBatteryConsumersCursorWindow; 181 182 double totalPowerMah = 0; 183 final int uidBatteryConsumerCount = builder.mUidBatteryConsumerBuilders.size(); 184 mUidBatteryConsumers = new ArrayList<>(uidBatteryConsumerCount); 185 for (int i = 0; i < uidBatteryConsumerCount; i++) { 186 final UidBatteryConsumer.Builder uidBatteryConsumerBuilder = 187 builder.mUidBatteryConsumerBuilders.valueAt(i); 188 if (!uidBatteryConsumerBuilder.isExcludedFromBatteryUsageStats()) { 189 final UidBatteryConsumer consumer = uidBatteryConsumerBuilder.build(); 190 totalPowerMah += consumer.getConsumedPower(); 191 mUidBatteryConsumers.add(consumer); 192 } 193 } 194 195 final int userBatteryConsumerCount = builder.mUserBatteryConsumerBuilders.size(); 196 mUserBatteryConsumers = new ArrayList<>(userBatteryConsumerCount); 197 for (int i = 0; i < userBatteryConsumerCount; i++) { 198 final UserBatteryConsumer consumer = 199 builder.mUserBatteryConsumerBuilders.valueAt(i).build(); 200 totalPowerMah += consumer.getConsumedPower(); 201 mUserBatteryConsumers.add(consumer); 202 } 203 204 builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) 205 .addConsumedPower(totalPowerMah); 206 207 mAggregateBatteryConsumers = 208 new AggregateBatteryConsumer[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT]; 209 for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) { 210 mAggregateBatteryConsumers[i] = builder.mAggregateBatteryConsumersBuilders[i].build(); 211 } 212 } 213 214 /** 215 * Timestamp (as returned by System.currentTimeMillis()) of the latest battery stats reset, in 216 * milliseconds. 217 */ getStatsStartTimestamp()218 public long getStatsStartTimestamp() { 219 return mStatsStartTimestampMs; 220 } 221 222 /** 223 * Timestamp (as returned by System.currentTimeMillis()) of when the stats snapshot was taken, 224 * in milliseconds. 225 */ getStatsEndTimestamp()226 public long getStatsEndTimestamp() { 227 return mStatsEndTimestampMs; 228 } 229 230 /** 231 * Returns the duration of the stats session captured by this BatteryUsageStats. 232 * In rare cases, statsDuration != statsEndTimestamp - statsStartTimestamp. This may 233 * happen when BatteryUsageStats represents an accumulation of data across multiple 234 * non-contiguous sessions. 235 */ getStatsDuration()236 public long getStatsDuration() { 237 return mStatsDurationMs; 238 } 239 240 /** 241 * Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully 242 * charged), in mAh 243 */ getConsumedPower()244 public double getConsumedPower() { 245 return mAggregateBatteryConsumers[AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE] 246 .getConsumedPower(); 247 } 248 249 /** 250 * Returns battery capacity in milli-amp-hours. 251 */ getBatteryCapacity()252 public double getBatteryCapacity() { 253 return mBatteryCapacityMah; 254 } 255 256 /** 257 * Portion of battery charge drained since BatteryStats reset (e.g. due to being fully 258 * charged), as percentage of the full charge in the range [0:100]. May exceed 100 if 259 * the device repeatedly charged and discharged prior to the reset. 260 */ getDischargePercentage()261 public int getDischargePercentage() { 262 return mDischargePercentage; 263 } 264 265 /** 266 * Returns the discharged power since BatteryStats were last reset, in mAh as an estimated 267 * range. 268 */ getDischargedPowerRange()269 public Range<Double> getDischargedPowerRange() { 270 return Range.create(mDischargedPowerLowerBound, mDischargedPowerUpperBound); 271 } 272 273 /** 274 * Returns the total amount of time the battery was discharging. 275 */ getDischargeDurationMs()276 public long getDischargeDurationMs() { 277 return mDischargeDurationMs; 278 } 279 280 /** 281 * Returns an approximation for how much run time (in milliseconds) is remaining on 282 * the battery. Returns -1 if no time can be computed: either there is not 283 * enough current data to make a decision, or the battery is currently 284 * charging. 285 */ getBatteryTimeRemainingMs()286 public long getBatteryTimeRemainingMs() { 287 return mBatteryTimeRemainingMs; 288 } 289 290 /** 291 * Returns an approximation for how much time (in milliseconds) remains until the battery 292 * is fully charged. Returns -1 if no time can be computed: either there is not 293 * enough current data to make a decision, or the battery is currently discharging. 294 */ getChargeTimeRemainingMs()295 public long getChargeTimeRemainingMs() { 296 return mChargeTimeRemainingMs; 297 } 298 299 /** 300 * Returns a battery consumer for the specified battery consumer type. 301 */ getAggregateBatteryConsumer( @ggregateBatteryConsumerScope int scope)302 public AggregateBatteryConsumer getAggregateBatteryConsumer( 303 @AggregateBatteryConsumerScope int scope) { 304 return mAggregateBatteryConsumers[scope]; 305 } 306 307 @NonNull getUidBatteryConsumers()308 public List<UidBatteryConsumer> getUidBatteryConsumers() { 309 return mUidBatteryConsumers; 310 } 311 312 @NonNull getUserBatteryConsumers()313 public List<UserBatteryConsumer> getUserBatteryConsumers() { 314 return mUserBatteryConsumers; 315 } 316 317 /** 318 * Returns the names of custom power components in order, so the first name in the array 319 * corresponds to the custom componentId 320 * {@link BatteryConsumer#FIRST_CUSTOM_POWER_COMPONENT_ID}. 321 */ 322 @NonNull getCustomPowerComponentNames()323 public String[] getCustomPowerComponentNames() { 324 return mCustomPowerComponentNames; 325 } 326 isProcessStateDataIncluded()327 public boolean isProcessStateDataIncluded() { 328 return mIncludesProcessStateData; 329 } 330 331 /** 332 * Returns an iterator for {@link android.os.BatteryStats.HistoryItem}'s. 333 */ 334 @NonNull iterateBatteryStatsHistory()335 public BatteryStatsHistoryIterator iterateBatteryStatsHistory() { 336 if (mBatteryStatsHistory == null) { 337 throw new IllegalStateException( 338 "Battery history was not requested in the BatteryUsageStatsQuery"); 339 } 340 return new BatteryStatsHistoryIterator(mBatteryStatsHistory, 0, MonotonicClock.UNDEFINED); 341 } 342 343 @Override describeContents()344 public int describeContents() { 345 return 0; 346 } 347 BatteryUsageStats(@onNull Parcel source)348 private BatteryUsageStats(@NonNull Parcel source) { 349 mStatsStartTimestampMs = source.readLong(); 350 mStatsEndTimestampMs = source.readLong(); 351 mStatsDurationMs = source.readLong(); 352 mBatteryCapacityMah = source.readDouble(); 353 mDischargePercentage = source.readInt(); 354 mDischargedPowerLowerBound = source.readDouble(); 355 mDischargedPowerUpperBound = source.readDouble(); 356 mDischargeDurationMs = source.readLong(); 357 mBatteryTimeRemainingMs = source.readLong(); 358 mChargeTimeRemainingMs = source.readLong(); 359 mCustomPowerComponentNames = source.readStringArray(); 360 mIncludesProcessStateData = source.readBoolean(); 361 mIncludesScreenStateData = source.readBoolean(); 362 mIncludesPowerStateData = source.readBoolean(); 363 364 mBatteryConsumersCursorWindow = CursorWindow.newFromParcel(source); 365 mBatteryConsumerDataLayout = BatteryConsumer.createBatteryConsumerDataLayout( 366 mCustomPowerComponentNames, mIncludesProcessStateData, 367 mIncludesScreenStateData, mIncludesPowerStateData); 368 369 final int numRows = mBatteryConsumersCursorWindow.getNumRows(); 370 371 mAggregateBatteryConsumers = 372 new AggregateBatteryConsumer[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT]; 373 mUidBatteryConsumers = new ArrayList<>(numRows); 374 mUserBatteryConsumers = new ArrayList<>(); 375 376 for (int i = 0; i < numRows; i++) { 377 final BatteryConsumer.BatteryConsumerData data = 378 new BatteryConsumer.BatteryConsumerData(mBatteryConsumersCursorWindow, i, 379 mBatteryConsumerDataLayout); 380 381 int consumerType = mBatteryConsumersCursorWindow.getInt(i, 382 BatteryConsumer.COLUMN_INDEX_BATTERY_CONSUMER_TYPE); 383 switch (consumerType) { 384 case AggregateBatteryConsumer.CONSUMER_TYPE_AGGREGATE: { 385 final AggregateBatteryConsumer consumer = new AggregateBatteryConsumer(data); 386 mAggregateBatteryConsumers[consumer.getScope()] = consumer; 387 break; 388 } 389 case UidBatteryConsumer.CONSUMER_TYPE_UID: { 390 mUidBatteryConsumers.add(new UidBatteryConsumer(data)); 391 break; 392 } 393 case UserBatteryConsumer.CONSUMER_TYPE_USER: 394 mUserBatteryConsumers.add(new UserBatteryConsumer(data)); 395 break; 396 } 397 } 398 399 if (source.readBoolean()) { 400 mBatteryStatsHistory = BatteryStatsHistory.createFromBatteryUsageStatsParcel(source); 401 mPreferredHistoryDurationMs = source.readLong(); 402 } else { 403 mBatteryStatsHistory = null; 404 mPreferredHistoryDurationMs = 0; 405 } 406 } 407 408 @Override writeToParcel(@onNull Parcel dest, int flags)409 public void writeToParcel(@NonNull Parcel dest, int flags) { 410 dest.writeLong(mStatsStartTimestampMs); 411 dest.writeLong(mStatsEndTimestampMs); 412 dest.writeLong(mStatsDurationMs); 413 dest.writeDouble(mBatteryCapacityMah); 414 dest.writeInt(mDischargePercentage); 415 dest.writeDouble(mDischargedPowerLowerBound); 416 dest.writeDouble(mDischargedPowerUpperBound); 417 dest.writeLong(mDischargeDurationMs); 418 dest.writeLong(mBatteryTimeRemainingMs); 419 dest.writeLong(mChargeTimeRemainingMs); 420 dest.writeStringArray(mCustomPowerComponentNames); 421 dest.writeBoolean(mIncludesProcessStateData); 422 dest.writeBoolean(mIncludesScreenStateData); 423 dest.writeBoolean(mIncludesPowerStateData); 424 425 mBatteryConsumersCursorWindow.writeToParcel(dest, flags); 426 427 if (mBatteryStatsHistory != null) { 428 dest.writeBoolean(true); 429 mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest, mPreferredHistoryDurationMs); 430 } else { 431 dest.writeBoolean(false); 432 } 433 } 434 435 @NonNull 436 public static final Creator<BatteryUsageStats> CREATOR = new Creator<BatteryUsageStats>() { 437 public BatteryUsageStats createFromParcel(@NonNull Parcel source) { 438 return new BatteryUsageStats(source); 439 } 440 441 public BatteryUsageStats[] newArray(int size) { 442 return new BatteryUsageStats[size]; 443 } 444 }; 445 446 /** Returns a proto (as used for atoms.proto) corresponding to this BatteryUsageStats. */ getStatsProto()447 public byte[] getStatsProto() { 448 // ProtoOutputStream.getRawSize() returns the buffer size before compaction. 449 // BatteryUsageStats contains a lot of integers, so compaction of integers to 450 // varint reduces the size of the proto buffer by as much as 50%. 451 int maxRawSize = (int) (STATSD_PULL_ATOM_MAX_BYTES * 1.75); 452 // Limit the number of attempts in order to prevent an infinite loop 453 for (int i = 0; i < 3; i++) { 454 final ProtoOutputStream proto = new ProtoOutputStream(); 455 writeStatsProto(proto, maxRawSize); 456 457 final int rawSize = proto.getRawSize(); 458 final byte[] protoOutput = proto.getBytes(); 459 460 if (protoOutput.length <= STATSD_PULL_ATOM_MAX_BYTES) { 461 return protoOutput; 462 } 463 464 // Adjust maxRawSize proportionately and try again. 465 maxRawSize = 466 (int) ((long) STATSD_PULL_ATOM_MAX_BYTES * rawSize / protoOutput.length - 1024); 467 } 468 469 // Fallback: if we have failed to generate a proto smaller than STATSD_PULL_ATOM_MAX_BYTES, 470 // just generate a proto with the _rawSize_ of STATSD_PULL_ATOM_MAX_BYTES, which is 471 // guaranteed to produce a compacted proto (significantly) smaller than 472 // STATSD_PULL_ATOM_MAX_BYTES. 473 final ProtoOutputStream proto = new ProtoOutputStream(); 474 writeStatsProto(proto, STATSD_PULL_ATOM_MAX_BYTES); 475 return proto.getBytes(); 476 } 477 478 /** 479 * Writes contents in a binary protobuffer format, using 480 * the android.os.BatteryUsageStatsAtomsProto proto. 481 */ dumpToProto(FileDescriptor fd)482 public void dumpToProto(FileDescriptor fd) { 483 final ProtoOutputStream proto = new ProtoOutputStream(fd); 484 writeStatsProto(proto, /* max size */ Integer.MAX_VALUE); 485 proto.flush(); 486 } 487 488 @NonNull writeStatsProto(ProtoOutputStream proto, int maxRawSize)489 private void writeStatsProto(ProtoOutputStream proto, int maxRawSize) { 490 final AggregateBatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer( 491 AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); 492 493 proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, getStatsStartTimestamp()); 494 proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, getStatsEndTimestamp()); 495 proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, getStatsDuration()); 496 proto.write(BatteryUsageStatsAtomsProto.SESSION_DISCHARGE_PERCENTAGE, 497 getDischargePercentage()); 498 proto.write(BatteryUsageStatsAtomsProto.DISCHARGE_DURATION_MILLIS, 499 getDischargeDurationMs()); 500 deviceBatteryConsumer.writeStatsProto(proto, 501 BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER); 502 writeUidBatteryConsumersProto(proto, maxRawSize); 503 } 504 505 /** 506 * Writes the UidBatteryConsumers data, held by this BatteryUsageStats, to the proto (as used 507 * for atoms.proto). 508 */ writeUidBatteryConsumersProto(ProtoOutputStream proto, int maxRawSize)509 private void writeUidBatteryConsumersProto(ProtoOutputStream proto, int maxRawSize) { 510 final List<UidBatteryConsumer> consumers = getUidBatteryConsumers(); 511 // Order consumers by descending weight (a combination of consumed power and usage time) 512 consumers.sort(Comparator.comparingDouble(this::getUidBatteryConsumerWeight).reversed()); 513 514 final int size = consumers.size(); 515 for (int i = 0; i < size; i++) { 516 final UidBatteryConsumer consumer = consumers.get(i); 517 518 final long fgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND); 519 final long bgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND); 520 final boolean hasBaseData = consumer.hasStatsProtoData(); 521 522 if (fgMs == 0 && bgMs == 0 && !hasBaseData) { 523 continue; 524 } 525 526 final long token = proto.start(BatteryUsageStatsAtomsProto.UID_BATTERY_CONSUMERS); 527 proto.write( 528 BatteryUsageStatsAtomsProto.UidBatteryConsumer.UID, 529 consumer.getUid()); 530 if (hasBaseData) { 531 consumer.writeStatsProto(proto, 532 BatteryUsageStatsAtomsProto.UidBatteryConsumer.BATTERY_CONSUMER_DATA); 533 } 534 proto.write( 535 BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_FOREGROUND_MILLIS, 536 fgMs); 537 proto.write( 538 BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_BACKGROUND_MILLIS, 539 bgMs); 540 for (int processState : UID_USAGE_TIME_PROCESS_STATES) { 541 final long timeInStateMillis = consumer.getTimeInProcessStateMs(processState); 542 if (timeInStateMillis <= 0) { 543 continue; 544 } 545 final long timeInStateToken = proto.start( 546 BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_STATE); 547 proto.write( 548 BatteryUsageStatsAtomsProto.UidBatteryConsumer.TimeInState.PROCESS_STATE, 549 processState); 550 proto.write( 551 BatteryUsageStatsAtomsProto.UidBatteryConsumer.TimeInState 552 .TIME_IN_STATE_MILLIS, 553 timeInStateMillis); 554 proto.end(timeInStateToken); 555 } 556 proto.end(token); 557 558 if (proto.getRawSize() >= maxRawSize) { 559 break; 560 } 561 } 562 } 563 564 private static final double WEIGHT_CONSUMED_POWER = 1; 565 // Weight one hour in foreground the same as 100 mAh of power drain 566 private static final double WEIGHT_FOREGROUND_STATE = 100.0 / (1 * 60 * 60 * 1000); 567 // Weight one hour in background the same as 300 mAh of power drain 568 private static final double WEIGHT_BACKGROUND_STATE = 300.0 / (1 * 60 * 60 * 1000); 569 570 /** 571 * Computes the weight associated with a UidBatteryConsumer, which is used for sorting. 572 * We want applications with the largest consumed power as well as applications 573 * with the highest usage time to be included in the statsd atom. 574 */ getUidBatteryConsumerWeight(UidBatteryConsumer uidBatteryConsumer)575 private double getUidBatteryConsumerWeight(UidBatteryConsumer uidBatteryConsumer) { 576 final double consumedPower = uidBatteryConsumer.getConsumedPower(); 577 final long timeInForeground = 578 uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND); 579 final long timeInBackground = 580 uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND); 581 return consumedPower * WEIGHT_CONSUMED_POWER 582 + timeInForeground * WEIGHT_FOREGROUND_STATE 583 + timeInBackground * WEIGHT_BACKGROUND_STATE; 584 } 585 586 /** 587 * Prints the stats in a human-readable format. 588 */ dump(PrintWriter pw, String prefix)589 public void dump(PrintWriter pw, String prefix) { 590 pw.print(prefix); 591 pw.println(" Estimated power use (mAh):"); 592 pw.print(prefix); 593 pw.print(" Capacity: "); 594 pw.print(BatteryStats.formatCharge(getBatteryCapacity())); 595 pw.print(", Computed drain: "); 596 pw.print(BatteryStats.formatCharge(getConsumedPower())); 597 final Range<Double> dischargedPowerRange = getDischargedPowerRange(); 598 pw.print(", actual drain: "); 599 pw.print(BatteryStats.formatCharge(dischargedPowerRange.getLower())); 600 if (!dischargedPowerRange.getLower().equals(dischargedPowerRange.getUpper())) { 601 pw.print("-"); 602 pw.print(BatteryStats.formatCharge(dischargedPowerRange.getUpper())); 603 } 604 pw.println(); 605 606 pw.println(" Global"); 607 final BatteryConsumer deviceConsumer = getAggregateBatteryConsumer( 608 AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); 609 final BatteryConsumer appsConsumer = getAggregateBatteryConsumer( 610 AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS); 611 612 for (@BatteryConsumer.PowerComponentId int powerComponent : 613 mBatteryConsumerDataLayout.powerComponentIds) { 614 final double devicePowerMah = deviceConsumer.getConsumedPower(powerComponent); 615 final double appsPowerMah = appsConsumer.getConsumedPower(powerComponent); 616 if (devicePowerMah == 0 && appsPowerMah == 0) { 617 continue; 618 } 619 620 printPowerComponent(pw, prefix, 621 mBatteryConsumerDataLayout.getPowerComponentName(powerComponent), 622 devicePowerMah, appsPowerMah, 623 deviceConsumer.getUsageDurationMillis(powerComponent)); 624 } 625 626 String prefixPlus = prefix + " "; 627 if (mIncludesPowerStateData && !mIncludesScreenStateData) { 628 for (@BatteryConsumer.PowerState int powerState = 0; 629 powerState < BatteryConsumer.POWER_STATE_COUNT; 630 powerState++) { 631 if (powerState != BatteryConsumer.POWER_STATE_UNSPECIFIED) { 632 dumpPowerComponents(pw, BatteryConsumer.SCREEN_STATE_ANY, powerState, 633 prefixPlus); 634 } 635 } 636 } else if (!mIncludesPowerStateData && mIncludesScreenStateData) { 637 for (@BatteryConsumer.ScreenState int screenState = 0; 638 screenState < BatteryConsumer.SCREEN_STATE_COUNT; 639 screenState++) { 640 if (screenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED) { 641 dumpPowerComponents(pw, screenState, BatteryConsumer.POWER_STATE_ANY, 642 prefixPlus); 643 } 644 } 645 } else if (mIncludesPowerStateData && mIncludesScreenStateData) { 646 for (@BatteryConsumer.PowerState int powerState = 0; 647 powerState < BatteryConsumer.POWER_STATE_COUNT; 648 powerState++) { 649 if (powerState != BatteryConsumer.POWER_STATE_UNSPECIFIED) { 650 for (@BatteryConsumer.ScreenState int screenState = 0; 651 screenState < BatteryConsumer.SCREEN_STATE_COUNT; screenState++) { 652 if (screenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED) { 653 dumpPowerComponents(pw, screenState, powerState, prefixPlus); 654 } 655 } 656 } 657 } 658 } 659 660 dumpSortedBatteryConsumers(pw, prefix, getUidBatteryConsumers()); 661 dumpSortedBatteryConsumers(pw, prefix, getUserBatteryConsumers()); 662 pw.println(); 663 } 664 dumpPowerComponents(PrintWriter pw, @BatteryConsumer.ScreenState int screenState, @BatteryConsumer.PowerState int powerState, String prefix)665 private void dumpPowerComponents(PrintWriter pw, 666 @BatteryConsumer.ScreenState int screenState, 667 @BatteryConsumer.PowerState int powerState, String prefix) { 668 final BatteryConsumer deviceConsumer = getAggregateBatteryConsumer( 669 AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); 670 final BatteryConsumer appsConsumer = getAggregateBatteryConsumer( 671 AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS); 672 673 boolean labelPrinted = false; 674 for (@BatteryConsumer.PowerComponentId int powerComponent : 675 mBatteryConsumerDataLayout.powerComponentIds) { 676 BatteryConsumer.Dimensions dimensions = new BatteryConsumer.Dimensions( 677 powerComponent, BatteryConsumer.PROCESS_STATE_ANY, screenState, powerState); 678 final double devicePowerMah = deviceConsumer.getConsumedPower(dimensions); 679 final double appsPowerMah = appsConsumer.getConsumedPower(dimensions); 680 if (devicePowerMah == 0 && appsPowerMah == 0) { 681 continue; 682 } 683 684 if (!labelPrinted) { 685 boolean empty = true; 686 StringBuilder stateLabel = new StringBuilder(); 687 stateLabel.append(" ("); 688 if (powerState != BatteryConsumer.POWER_STATE_ANY) { 689 stateLabel.append(BatteryConsumer.powerStateToString(powerState)); 690 empty = false; 691 } 692 if (screenState != BatteryConsumer.SCREEN_STATE_ANY) { 693 if (!empty) { 694 stateLabel.append(", "); 695 } 696 stateLabel.append("screen ") 697 .append(BatteryConsumer.screenStateToString(screenState)); 698 empty = false; 699 } 700 if (!empty) { 701 stateLabel.append(")"); 702 pw.println(stateLabel); 703 labelPrinted = true; 704 } 705 } 706 printPowerComponent(pw, prefix, 707 mBatteryConsumerDataLayout.getPowerComponentName(powerComponent), 708 devicePowerMah, appsPowerMah, 709 deviceConsumer.getUsageDurationMillis(dimensions)); 710 } 711 } 712 printPowerComponent(PrintWriter pw, String prefix, String label, double devicePowerMah, double appsPowerMah, long durationMs)713 private void printPowerComponent(PrintWriter pw, String prefix, String label, 714 double devicePowerMah, double appsPowerMah, long durationMs) { 715 StringBuilder sb = new StringBuilder(); 716 sb.append(prefix).append(" ").append(label).append(": ") 717 .append(BatteryStats.formatCharge(devicePowerMah)); 718 sb.append(" apps: ").append(BatteryStats.formatCharge(appsPowerMah)); 719 if (durationMs != 0) { 720 sb.append(" duration: "); 721 BatteryStats.formatTimeMs(sb, durationMs); 722 } 723 724 pw.println(sb); 725 } 726 dumpSortedBatteryConsumers(PrintWriter pw, String prefix, List<? extends BatteryConsumer> batteryConsumers)727 private void dumpSortedBatteryConsumers(PrintWriter pw, String prefix, 728 List<? extends BatteryConsumer> batteryConsumers) { 729 batteryConsumers.sort( 730 Comparator.<BatteryConsumer>comparingDouble(BatteryConsumer::getConsumedPower) 731 .reversed()); 732 for (BatteryConsumer consumer : batteryConsumers) { 733 if (consumer.getConsumedPower() == 0) { 734 continue; 735 } 736 pw.print(prefix); 737 pw.print(" "); 738 consumer.dump(pw); 739 } 740 } 741 742 /** Serializes this object to XML */ writeXml(TypedXmlSerializer serializer)743 public void writeXml(TypedXmlSerializer serializer) throws IOException { 744 serializer.startTag(null, XML_TAG_BATTERY_USAGE_STATS); 745 746 for (int i = 0; i < mCustomPowerComponentNames.length; i++) { 747 serializer.attribute(null, XML_ATTR_PREFIX_CUSTOM_COMPONENT + i, 748 mCustomPowerComponentNames[i]); 749 } 750 serializer.attributeBoolean(null, XML_ATTR_PREFIX_INCLUDES_PROC_STATE_DATA, 751 mIncludesProcessStateData); 752 serializer.attributeBoolean(null, XML_ATTR_PREFIX_INCLUDES_SCREEN_STATE_DATA, 753 mIncludesScreenStateData); 754 serializer.attributeBoolean(null, XML_ATTR_PREFIX_INCLUDES_POWER_STATE_DATA, 755 mIncludesPowerStateData); 756 serializer.attributeLong(null, XML_ATTR_START_TIMESTAMP, mStatsStartTimestampMs); 757 serializer.attributeLong(null, XML_ATTR_END_TIMESTAMP, mStatsEndTimestampMs); 758 serializer.attributeLong(null, XML_ATTR_DURATION, mStatsDurationMs); 759 serializer.attributeDouble(null, XML_ATTR_BATTERY_CAPACITY, mBatteryCapacityMah); 760 serializer.attributeInt(null, XML_ATTR_DISCHARGE_PERCENT, mDischargePercentage); 761 serializer.attributeDouble(null, XML_ATTR_DISCHARGE_LOWER, mDischargedPowerLowerBound); 762 serializer.attributeDouble(null, XML_ATTR_DISCHARGE_UPPER, mDischargedPowerUpperBound); 763 serializer.attributeLong(null, XML_ATTR_DISCHARGE_DURATION, mDischargeDurationMs); 764 serializer.attributeLong(null, XML_ATTR_BATTERY_REMAINING, mBatteryTimeRemainingMs); 765 serializer.attributeLong(null, XML_ATTR_CHARGE_REMAINING, mChargeTimeRemainingMs); 766 767 for (int scope = 0; scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; 768 scope++) { 769 mAggregateBatteryConsumers[scope].writeToXml(serializer, scope); 770 } 771 for (UidBatteryConsumer consumer : mUidBatteryConsumers) { 772 consumer.writeToXml(serializer); 773 } 774 for (UserBatteryConsumer consumer : mUserBatteryConsumers) { 775 consumer.writeToXml(serializer); 776 } 777 serializer.endTag(null, XML_TAG_BATTERY_USAGE_STATS); 778 } 779 780 /** Parses an XML representation of BatteryUsageStats */ createFromXml(TypedXmlPullParser parser)781 public static BatteryUsageStats createFromXml(TypedXmlPullParser parser) 782 throws XmlPullParserException, IOException { 783 return createBuilderFromXml(parser).build(); 784 } 785 786 /** Parses an XML representation of BatteryUsageStats */ createBuilderFromXml(TypedXmlPullParser parser)787 public static BatteryUsageStats.Builder createBuilderFromXml(TypedXmlPullParser parser) 788 throws XmlPullParserException, IOException { 789 Builder builder = null; 790 int eventType = parser.getEventType(); 791 while (eventType != XmlPullParser.END_DOCUMENT) { 792 if (eventType == XmlPullParser.START_TAG 793 && parser.getName().equals(XML_TAG_BATTERY_USAGE_STATS)) { 794 List<String> customComponentNames = new ArrayList<>(); 795 int i = 0; 796 while (true) { 797 int index = parser.getAttributeIndex(null, 798 XML_ATTR_PREFIX_CUSTOM_COMPONENT + i); 799 if (index == -1) { 800 break; 801 } 802 customComponentNames.add(parser.getAttributeValue(index)); 803 i++; 804 } 805 806 final boolean includesProcStateData = parser.getAttributeBoolean(null, 807 XML_ATTR_PREFIX_INCLUDES_PROC_STATE_DATA, false); 808 final boolean includesScreenStateData = parser.getAttributeBoolean(null, 809 XML_ATTR_PREFIX_INCLUDES_SCREEN_STATE_DATA, false); 810 final boolean includesPowerStateData = parser.getAttributeBoolean(null, 811 XML_ATTR_PREFIX_INCLUDES_POWER_STATE_DATA, false); 812 813 builder = new Builder(customComponentNames.toArray(new String[0]), 814 includesProcStateData, includesScreenStateData, includesPowerStateData, 0); 815 816 builder.setStatsStartTimestamp( 817 parser.getAttributeLong(null, XML_ATTR_START_TIMESTAMP)); 818 builder.setStatsEndTimestamp( 819 parser.getAttributeLong(null, XML_ATTR_END_TIMESTAMP)); 820 builder.setStatsDuration( 821 parser.getAttributeLong(null, XML_ATTR_DURATION)); 822 builder.setBatteryCapacity( 823 parser.getAttributeDouble(null, XML_ATTR_BATTERY_CAPACITY)); 824 builder.addDischargePercentage( 825 parser.getAttributeInt(null, XML_ATTR_DISCHARGE_PERCENT)); 826 builder.addDischargedPowerRange( 827 parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_LOWER), 828 parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_UPPER)); 829 builder.addDischargeDurationMs( 830 parser.getAttributeLong(null, XML_ATTR_DISCHARGE_DURATION)); 831 builder.setBatteryTimeRemainingMs( 832 parser.getAttributeLong(null, XML_ATTR_BATTERY_REMAINING)); 833 builder.setChargeTimeRemainingMs( 834 parser.getAttributeLong(null, XML_ATTR_CHARGE_REMAINING)); 835 836 eventType = parser.next(); 837 break; 838 } 839 eventType = parser.next(); 840 } 841 842 if (builder == null) { 843 throw new XmlPullParserException("No root element"); 844 } 845 846 while (eventType != XmlPullParser.END_DOCUMENT) { 847 if (eventType == XmlPullParser.START_TAG) { 848 switch (parser.getName()) { 849 case XML_TAG_AGGREGATE: 850 AggregateBatteryConsumer.parseXml(parser, builder); 851 break; 852 case XML_TAG_UID: 853 UidBatteryConsumer.createFromXml(parser, builder); 854 break; 855 case XML_TAG_USER: 856 UserBatteryConsumer.createFromXml(parser, builder); 857 break; 858 } 859 } 860 eventType = parser.next(); 861 } 862 863 return builder; 864 } 865 866 @Override close()867 public void close() throws IOException { 868 onCursorWindowReleased(mBatteryConsumersCursorWindow); 869 mBatteryConsumersCursorWindow.close(); 870 mBatteryConsumersCursorWindow = null; 871 } 872 873 @Override finalize()874 protected void finalize() throws Throwable { 875 if (mBatteryConsumersCursorWindow != null) { 876 // Do not decrement sOpenCusorWindowCount. All instances should be closed explicitly 877 mBatteryConsumersCursorWindow.close(); 878 } 879 super.finalize(); 880 } 881 882 @Override toString()883 public String toString() { 884 StringWriter sw = new StringWriter(); 885 PrintWriter pw = new PrintWriter(sw); 886 dump(pw, ""); 887 pw.flush(); 888 return sw.toString(); 889 } 890 891 /** 892 * Builder for BatteryUsageStats. 893 */ 894 public static final class Builder { 895 private final CursorWindow mBatteryConsumersCursorWindow; 896 @NonNull 897 private final String[] mCustomPowerComponentNames; 898 private final boolean mIncludesProcessStateData; 899 private final boolean mIncludesScreenStateData; 900 private final boolean mIncludesPowerStateData; 901 private final double mMinConsumedPowerThreshold; 902 private final BatteryConsumer.BatteryConsumerDataLayout mBatteryConsumerDataLayout; 903 private long mStatsStartTimestampMs; 904 private long mStatsEndTimestampMs; 905 private long mStatsDurationMs = -1; 906 private double mBatteryCapacityMah; 907 private int mDischargePercentage; 908 private double mDischargedPowerLowerBoundMah; 909 private double mDischargedPowerUpperBoundMah; 910 private long mDischargeDurationMs; 911 private long mBatteryTimeRemainingMs = -1; 912 private long mChargeTimeRemainingMs = -1; 913 private final AggregateBatteryConsumer.Builder[] mAggregateBatteryConsumersBuilders = 914 new AggregateBatteryConsumer.Builder[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT]; 915 private final SparseArray<UidBatteryConsumer.Builder> mUidBatteryConsumerBuilders = 916 new SparseArray<>(); 917 private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders = 918 new SparseArray<>(); 919 private BatteryStatsHistory mBatteryStatsHistory; 920 private long mPreferredHistoryDurationMs; 921 Builder(@onNull String[] customPowerComponentNames)922 public Builder(@NonNull String[] customPowerComponentNames) { 923 this(customPowerComponentNames, false, false, false, 0); 924 } 925 Builder(@onNull String[] customPowerComponentNames, boolean includeProcessStateData, boolean includeScreenStateData, boolean includesPowerStateData, double minConsumedPowerThreshold)926 public Builder(@NonNull String[] customPowerComponentNames, 927 boolean includeProcessStateData, boolean includeScreenStateData, 928 boolean includesPowerStateData, double minConsumedPowerThreshold) { 929 mBatteryConsumersCursorWindow = 930 new CursorWindow(null, BATTERY_CONSUMER_CURSOR_WINDOW_SIZE); 931 onCursorWindowAllocated(mBatteryConsumersCursorWindow); 932 mBatteryConsumerDataLayout = BatteryConsumer.createBatteryConsumerDataLayout( 933 customPowerComponentNames, includeProcessStateData, 934 includeScreenStateData, includesPowerStateData); 935 mBatteryConsumersCursorWindow.setNumColumns(mBatteryConsumerDataLayout.columnCount); 936 937 mCustomPowerComponentNames = customPowerComponentNames; 938 mIncludesProcessStateData = includeProcessStateData; 939 mIncludesScreenStateData = includeScreenStateData; 940 mIncludesPowerStateData = includesPowerStateData; 941 mMinConsumedPowerThreshold = minConsumedPowerThreshold; 942 for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) { 943 final BatteryConsumer.BatteryConsumerData data = 944 BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow, 945 mBatteryConsumerDataLayout); 946 mAggregateBatteryConsumersBuilders[scope] = 947 new AggregateBatteryConsumer.Builder( 948 data, scope, mMinConsumedPowerThreshold); 949 } 950 } 951 isProcessStateDataNeeded()952 public boolean isProcessStateDataNeeded() { 953 return mIncludesProcessStateData; 954 } 955 isScreenStateDataNeeded()956 public boolean isScreenStateDataNeeded() { 957 return mIncludesScreenStateData; 958 } 959 isPowerStateDataNeeded()960 public boolean isPowerStateDataNeeded() { 961 return mIncludesPowerStateData; 962 } 963 964 /** 965 * Returns true if this Builder is configured to hold data for the specified 966 * power component index. 967 */ isSupportedPowerComponent( @atteryConsumer.PowerComponentId int componentId)968 public boolean isSupportedPowerComponent( 969 @BatteryConsumer.PowerComponentId int componentId) { 970 return componentId < BatteryConsumer.POWER_COMPONENT_COUNT 971 || (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID 972 && componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID 973 + mBatteryConsumerDataLayout.customPowerComponentCount); 974 } 975 976 /** 977 * Constructs a read-only object using the Builder values. 978 */ 979 @NonNull build()980 public BatteryUsageStats build() { 981 if (mBatteryConsumersCursorWindow == null) { 982 throw new IllegalStateException("Builder has been discarded"); 983 } 984 return new BatteryUsageStats(this); 985 } 986 987 /** 988 * Close this builder without actually calling ".build()". Do not attempt 989 * to continue using the builder after this call. 990 */ discard()991 public void discard() { 992 mBatteryConsumersCursorWindow.close(); 993 onCursorWindowReleased(mBatteryConsumersCursorWindow); 994 } 995 996 /** 997 * Sets the battery capacity in milli-amp-hours. 998 */ setBatteryCapacity(double batteryCapacityMah)999 public Builder setBatteryCapacity(double batteryCapacityMah) { 1000 mBatteryCapacityMah = batteryCapacityMah; 1001 return this; 1002 } 1003 1004 /** 1005 * Sets the timestamp of the latest battery stats reset, in milliseconds. 1006 */ setStatsStartTimestamp(long statsStartTimestampMs)1007 public Builder setStatsStartTimestamp(long statsStartTimestampMs) { 1008 mStatsStartTimestampMs = statsStartTimestampMs; 1009 return this; 1010 } 1011 1012 /** 1013 * Sets the timestamp of when the battery stats snapshot was taken, in milliseconds. 1014 */ setStatsEndTimestamp(long statsEndTimestampMs)1015 public Builder setStatsEndTimestamp(long statsEndTimestampMs) { 1016 mStatsEndTimestampMs = statsEndTimestampMs; 1017 return this; 1018 } 1019 1020 /** 1021 * Sets the duration of the stats session. The default value of this field is 1022 * statsEndTimestamp - statsStartTimestamp. 1023 */ setStatsDuration(long statsDurationMs)1024 public Builder setStatsDuration(long statsDurationMs) { 1025 mStatsDurationMs = statsDurationMs; 1026 return this; 1027 } 1028 1029 /** 1030 * Returns the duration of the battery session reflected by these stats. 1031 */ getStatsDuration()1032 public long getStatsDuration() { 1033 if (mStatsDurationMs != -1) { 1034 return mStatsDurationMs; 1035 } else { 1036 return mStatsEndTimestampMs - mStatsStartTimestampMs; 1037 } 1038 } 1039 1040 /** 1041 * Accumulates the battery discharge amount as percentage of the full charge. Can exceed 100 1042 */ 1043 @NonNull addDischargePercentage(int dischargePercentage)1044 public Builder addDischargePercentage(int dischargePercentage) { 1045 mDischargePercentage += dischargePercentage; 1046 return this; 1047 } 1048 1049 /** 1050 * Accumulates the estimated battery discharge range. 1051 */ 1052 @NonNull addDischargedPowerRange(double dischargedPowerLowerBoundMah, double dischargedPowerUpperBoundMah)1053 public Builder addDischargedPowerRange(double dischargedPowerLowerBoundMah, 1054 double dischargedPowerUpperBoundMah) { 1055 mDischargedPowerLowerBoundMah += dischargedPowerLowerBoundMah; 1056 mDischargedPowerUpperBoundMah += dischargedPowerUpperBoundMah; 1057 return this; 1058 } 1059 1060 /** 1061 * Sets the total battery discharge time, in milliseconds. 1062 */ 1063 @NonNull addDischargeDurationMs(long durationMs)1064 public Builder addDischargeDurationMs(long durationMs) { 1065 mDischargeDurationMs += durationMs; 1066 return this; 1067 } 1068 1069 /** 1070 * Sets an approximation for how much time (in milliseconds) remains until the battery 1071 * is fully discharged. 1072 */ 1073 @NonNull setBatteryTimeRemainingMs(long batteryTimeRemainingMs)1074 public Builder setBatteryTimeRemainingMs(long batteryTimeRemainingMs) { 1075 mBatteryTimeRemainingMs = batteryTimeRemainingMs; 1076 return this; 1077 } 1078 1079 /** 1080 * Sets an approximation for how much time (in milliseconds) remains until the battery 1081 * is fully charged. 1082 */ 1083 @NonNull setChargeTimeRemainingMs(long chargeTimeRemainingMs)1084 public Builder setChargeTimeRemainingMs(long chargeTimeRemainingMs) { 1085 mChargeTimeRemainingMs = chargeTimeRemainingMs; 1086 return this; 1087 } 1088 1089 /** 1090 * Sets the parceled recent history. 1091 */ 1092 @NonNull setBatteryHistory(BatteryStatsHistory batteryStatsHistory, long preferredHistoryDurationMs)1093 public Builder setBatteryHistory(BatteryStatsHistory batteryStatsHistory, 1094 long preferredHistoryDurationMs) { 1095 mBatteryStatsHistory = batteryStatsHistory; 1096 mPreferredHistoryDurationMs = preferredHistoryDurationMs; 1097 return this; 1098 } 1099 1100 /** 1101 * Creates or returns an AggregateBatteryConsumer builder, which represents aggregate 1102 * battery consumption data for the specified scope. 1103 */ 1104 @NonNull getAggregateBatteryConsumerBuilder( @ggregateBatteryConsumerScope int scope)1105 public AggregateBatteryConsumer.Builder getAggregateBatteryConsumerBuilder( 1106 @AggregateBatteryConsumerScope int scope) { 1107 return mAggregateBatteryConsumersBuilders[scope]; 1108 } 1109 1110 /** 1111 * Creates or returns a UidBatteryConsumer, which represents battery attribution 1112 * data for an individual UID. 1113 */ 1114 @NonNull getOrCreateUidBatteryConsumerBuilder( @onNull BatteryStats.Uid batteryStatsUid)1115 public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder( 1116 @NonNull BatteryStats.Uid batteryStatsUid) { 1117 int uid = batteryStatsUid.getUid(); 1118 UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid); 1119 if (builder == null) { 1120 final BatteryConsumer.BatteryConsumerData data = 1121 BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow, 1122 mBatteryConsumerDataLayout); 1123 builder = new UidBatteryConsumer.Builder(data, batteryStatsUid, 1124 mMinConsumedPowerThreshold); 1125 mUidBatteryConsumerBuilders.put(uid, builder); 1126 } 1127 return builder; 1128 } 1129 1130 /** 1131 * Creates or returns a UidBatteryConsumer, which represents battery attribution 1132 * data for an individual UID. This version of the method is not suitable for use 1133 * with PowerCalculators. 1134 */ 1135 @NonNull getOrCreateUidBatteryConsumerBuilder(int uid)1136 public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder(int uid) { 1137 UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid); 1138 if (builder == null) { 1139 final BatteryConsumer.BatteryConsumerData data = 1140 BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow, 1141 mBatteryConsumerDataLayout); 1142 builder = new UidBatteryConsumer.Builder(data, uid, mMinConsumedPowerThreshold); 1143 mUidBatteryConsumerBuilders.put(uid, builder); 1144 } 1145 return builder; 1146 } 1147 1148 /** 1149 * Creates or returns a UserBatteryConsumer, which represents battery attribution 1150 * data for an individual {@link UserHandle}. 1151 */ 1152 @NonNull getOrCreateUserBatteryConsumerBuilder(int userId)1153 public UserBatteryConsumer.Builder getOrCreateUserBatteryConsumerBuilder(int userId) { 1154 UserBatteryConsumer.Builder builder = mUserBatteryConsumerBuilders.get(userId); 1155 if (builder == null) { 1156 final BatteryConsumer.BatteryConsumerData data = 1157 BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow, 1158 mBatteryConsumerDataLayout); 1159 builder = new UserBatteryConsumer.Builder(data, userId, mMinConsumedPowerThreshold); 1160 mUserBatteryConsumerBuilders.put(userId, builder); 1161 } 1162 return builder; 1163 } 1164 1165 @NonNull getUidBatteryConsumerBuilders()1166 public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() { 1167 return mUidBatteryConsumerBuilders; 1168 } 1169 1170 /** 1171 * Adds battery usage stats from another snapshots. The two snapshots are assumed to be 1172 * non-overlapping, meaning that the power consumption estimates and session durations 1173 * can be simply summed across the two snapshots. This remains true even if the timestamps 1174 * seem to indicate that the sessions are in fact overlapping: timestamps may be off as a 1175 * result of realtime clock adjustments by the user or the system. 1176 */ 1177 @NonNull add(BatteryUsageStats stats)1178 public Builder add(BatteryUsageStats stats) { 1179 if (!Arrays.equals(mCustomPowerComponentNames, stats.mCustomPowerComponentNames)) { 1180 throw new IllegalArgumentException( 1181 "BatteryUsageStats have different custom power components"); 1182 } 1183 1184 if (mIncludesProcessStateData && !stats.mIncludesProcessStateData) { 1185 throw new IllegalArgumentException( 1186 "Added BatteryUsageStats does not include process state data"); 1187 } 1188 1189 if (mUserBatteryConsumerBuilders.size() != 0 1190 || !stats.getUserBatteryConsumers().isEmpty()) { 1191 throw new UnsupportedOperationException( 1192 "Combining UserBatteryConsumers is not supported"); 1193 } 1194 1195 mDischargedPowerLowerBoundMah += stats.mDischargedPowerLowerBound; 1196 mDischargedPowerUpperBoundMah += stats.mDischargedPowerUpperBound; 1197 mDischargePercentage += stats.mDischargePercentage; 1198 mDischargeDurationMs += stats.mDischargeDurationMs; 1199 1200 mStatsDurationMs = getStatsDuration() + stats.getStatsDuration(); 1201 1202 if (mStatsStartTimestampMs == 0 1203 || stats.mStatsStartTimestampMs < mStatsStartTimestampMs) { 1204 mStatsStartTimestampMs = stats.mStatsStartTimestampMs; 1205 } 1206 1207 final boolean addingLaterSnapshot = stats.mStatsEndTimestampMs > mStatsEndTimestampMs; 1208 if (addingLaterSnapshot) { 1209 mStatsEndTimestampMs = stats.mStatsEndTimestampMs; 1210 } 1211 1212 for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) { 1213 getAggregateBatteryConsumerBuilder(scope) 1214 .add(stats.mAggregateBatteryConsumers[scope]); 1215 } 1216 1217 for (UidBatteryConsumer consumer : stats.getUidBatteryConsumers()) { 1218 getOrCreateUidBatteryConsumerBuilder(consumer.getUid()).add(consumer); 1219 } 1220 1221 if (addingLaterSnapshot) { 1222 mBatteryCapacityMah = stats.mBatteryCapacityMah; 1223 mBatteryTimeRemainingMs = stats.mBatteryTimeRemainingMs; 1224 mChargeTimeRemainingMs = stats.mChargeTimeRemainingMs; 1225 } 1226 1227 return this; 1228 } 1229 1230 /** 1231 * Dumps raw contents of the cursor window for debugging. 1232 */ dump(PrintWriter writer)1233 void dump(PrintWriter writer) { 1234 final int numRows = mBatteryConsumersCursorWindow.getNumRows(); 1235 int numColumns = mBatteryConsumerDataLayout.columnCount; 1236 for (int i = 0; i < numRows; i++) { 1237 StringBuilder sb = new StringBuilder(); 1238 for (int j = 0; j < numColumns; j++) { 1239 final int type = mBatteryConsumersCursorWindow.getType(i, j); 1240 switch (type) { 1241 case Cursor.FIELD_TYPE_NULL: 1242 sb.append("null, "); 1243 break; 1244 case Cursor.FIELD_TYPE_INTEGER: 1245 sb.append(mBatteryConsumersCursorWindow.getInt(i, j)).append(", "); 1246 break; 1247 case Cursor.FIELD_TYPE_FLOAT: 1248 sb.append(mBatteryConsumersCursorWindow.getFloat(i, j)).append(", "); 1249 break; 1250 case Cursor.FIELD_TYPE_STRING: 1251 sb.append(mBatteryConsumersCursorWindow.getString(i, j)).append(", "); 1252 break; 1253 case Cursor.FIELD_TYPE_BLOB: 1254 sb.append("BLOB, "); 1255 break; 1256 } 1257 } 1258 sb.setLength(sb.length() - 2); 1259 writer.println(sb); 1260 } 1261 } 1262 } 1263 1264 /* 1265 * Used by tests to ensure all BatteryUsageStats instances are closed. 1266 */ 1267 private static volatile boolean sInstanceLeakDetectionEnabled; 1268 1269 @GuardedBy("BatteryUsageStats.class") 1270 private static Map<CursorWindow, Exception> sInstances; 1271 onCursorWindowAllocated(CursorWindow window)1272 private static void onCursorWindowAllocated(CursorWindow window) { 1273 if (!sInstanceLeakDetectionEnabled) { 1274 return; 1275 } 1276 1277 synchronized (BatteryUsageStats.class) { 1278 if (sInstances == null) { 1279 sInstances = new HashMap<>(); 1280 } 1281 sInstances.put(window, new Exception()); 1282 } 1283 } 1284 onCursorWindowReleased(CursorWindow window)1285 private static void onCursorWindowReleased(CursorWindow window) { 1286 if (!sInstanceLeakDetectionEnabled) { 1287 return; 1288 } 1289 1290 synchronized (BatteryUsageStats.class) { 1291 sInstances.remove(window); 1292 } 1293 } 1294 1295 /** 1296 * Enables detection of leaked BatteryUsageStats instances, meaning instances that are created 1297 * but not closed during the test execution. 1298 */ 1299 @VisibleForTesting enableInstanceLeakDetection()1300 public static void enableInstanceLeakDetection() { 1301 sInstanceLeakDetectionEnabled = true; 1302 synchronized (BatteryUsageStats.class) { 1303 if (sInstances != null) { 1304 sInstances.clear(); 1305 } 1306 } 1307 } 1308 1309 /** 1310 * Used by tests to ensure all BatteryUsageStats instances are closed. 1311 */ 1312 @VisibleForTesting assertAllInstancesClosed()1313 public static void assertAllInstancesClosed() { 1314 if (!sInstanceLeakDetectionEnabled) { 1315 throw new IllegalStateException("Instance leak detection is not enabled"); 1316 } 1317 1318 synchronized (BatteryUsageStats.class) { 1319 if (sInstances != null && !sInstances.isEmpty()) { 1320 Exception callSite = sInstances.entrySet().iterator().next().getValue(); 1321 int count = sInstances.size(); 1322 sInstances.clear(); 1323 throw new IllegalStateException( 1324 "Instances of BatteryUsageStats not closed: " + count, callSite); 1325 } 1326 } 1327 } 1328 } 1329