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.annotation.Nullable; 22 import android.database.CursorWindow; 23 import android.util.Slog; 24 import android.util.proto.ProtoOutputStream; 25 26 import java.io.PrintWriter; 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 import java.util.ArrayList; 30 31 /** 32 * Interface for objects containing battery attribution data. 33 * 34 * @hide 35 */ 36 @android.ravenwood.annotation.RavenwoodKeepWholeClass 37 public abstract class BatteryConsumer { 38 39 private static final String TAG = "BatteryConsumer"; 40 41 /** 42 * Power usage component, describing the particular part of the system 43 * responsible for power drain. 44 * 45 * @hide 46 */ 47 @IntDef(prefix = {"POWER_COMPONENT_"}, value = { 48 POWER_COMPONENT_ANY, 49 POWER_COMPONENT_SCREEN, 50 POWER_COMPONENT_CPU, 51 POWER_COMPONENT_BLUETOOTH, 52 POWER_COMPONENT_CAMERA, 53 POWER_COMPONENT_AUDIO, 54 POWER_COMPONENT_VIDEO, 55 POWER_COMPONENT_FLASHLIGHT, 56 POWER_COMPONENT_MOBILE_RADIO, 57 POWER_COMPONENT_SYSTEM_SERVICES, 58 POWER_COMPONENT_SENSORS, 59 POWER_COMPONENT_GNSS, 60 POWER_COMPONENT_WIFI, 61 POWER_COMPONENT_WAKELOCK, 62 POWER_COMPONENT_MEMORY, 63 POWER_COMPONENT_PHONE, 64 POWER_COMPONENT_IDLE, 65 POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, 66 }) 67 @Retention(RetentionPolicy.SOURCE) 68 public static @interface PowerComponent { 69 } 70 71 public static final int POWER_COMPONENT_ANY = -1; 72 public static final int POWER_COMPONENT_SCREEN = OsProtoEnums.POWER_COMPONENT_SCREEN; // 0 73 public static final int POWER_COMPONENT_CPU = OsProtoEnums.POWER_COMPONENT_CPU; // 1 74 public static final int POWER_COMPONENT_BLUETOOTH = OsProtoEnums.POWER_COMPONENT_BLUETOOTH; // 2 75 public static final int POWER_COMPONENT_CAMERA = OsProtoEnums.POWER_COMPONENT_CAMERA; // 3 76 public static final int POWER_COMPONENT_AUDIO = OsProtoEnums.POWER_COMPONENT_AUDIO; // 4 77 public static final int POWER_COMPONENT_VIDEO = OsProtoEnums.POWER_COMPONENT_VIDEO; // 5 78 public static final int POWER_COMPONENT_FLASHLIGHT = 79 OsProtoEnums.POWER_COMPONENT_FLASHLIGHT; // 6 80 public static final int POWER_COMPONENT_SYSTEM_SERVICES = 81 OsProtoEnums.POWER_COMPONENT_SYSTEM_SERVICES; // 7 82 public static final int POWER_COMPONENT_MOBILE_RADIO = 83 OsProtoEnums.POWER_COMPONENT_MOBILE_RADIO; // 8 84 public static final int POWER_COMPONENT_SENSORS = OsProtoEnums.POWER_COMPONENT_SENSORS; // 9 85 public static final int POWER_COMPONENT_GNSS = OsProtoEnums.POWER_COMPONENT_GNSS; // 10 86 public static final int POWER_COMPONENT_WIFI = OsProtoEnums.POWER_COMPONENT_WIFI; // 11 87 public static final int POWER_COMPONENT_WAKELOCK = OsProtoEnums.POWER_COMPONENT_WAKELOCK; // 12 88 public static final int POWER_COMPONENT_MEMORY = OsProtoEnums.POWER_COMPONENT_MEMORY; // 13 89 public static final int POWER_COMPONENT_PHONE = OsProtoEnums.POWER_COMPONENT_PHONE; // 14 90 public static final int POWER_COMPONENT_AMBIENT_DISPLAY = 91 OsProtoEnums.POWER_COMPONENT_AMBIENT_DISPLAY; // 15 92 public static final int POWER_COMPONENT_IDLE = OsProtoEnums.POWER_COMPONENT_IDLE; // 16 93 // Power that is re-attributed to other battery consumers. For example, for System Server 94 // this represents the power attributed to apps requesting system services. 95 // The value should be negative or zero. 96 public static final int POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS = 97 OsProtoEnums.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS; // 17 98 99 public static final int POWER_COMPONENT_COUNT = 18; 100 101 public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000; 102 public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999; 103 104 private static final String[] sPowerComponentNames = new String[POWER_COMPONENT_COUNT]; 105 106 static { 107 // Assign individually to avoid future mismatch 108 sPowerComponentNames[POWER_COMPONENT_SCREEN] = "screen"; 109 sPowerComponentNames[POWER_COMPONENT_CPU] = "cpu"; 110 sPowerComponentNames[POWER_COMPONENT_BLUETOOTH] = "bluetooth"; 111 sPowerComponentNames[POWER_COMPONENT_CAMERA] = "camera"; 112 sPowerComponentNames[POWER_COMPONENT_AUDIO] = "audio"; 113 sPowerComponentNames[POWER_COMPONENT_VIDEO] = "video"; 114 sPowerComponentNames[POWER_COMPONENT_FLASHLIGHT] = "flashlight"; 115 sPowerComponentNames[POWER_COMPONENT_SYSTEM_SERVICES] = "system_services"; 116 sPowerComponentNames[POWER_COMPONENT_MOBILE_RADIO] = "mobile_radio"; 117 sPowerComponentNames[POWER_COMPONENT_SENSORS] = "sensors"; 118 sPowerComponentNames[POWER_COMPONENT_GNSS] = "gnss"; 119 sPowerComponentNames[POWER_COMPONENT_WIFI] = "wifi"; 120 sPowerComponentNames[POWER_COMPONENT_WAKELOCK] = "wakelock"; 121 sPowerComponentNames[POWER_COMPONENT_MEMORY] = "memory"; 122 sPowerComponentNames[POWER_COMPONENT_PHONE] = "phone"; 123 sPowerComponentNames[POWER_COMPONENT_AMBIENT_DISPLAY] = "ambient_display"; 124 sPowerComponentNames[POWER_COMPONENT_IDLE] = "idle"; 125 sPowerComponentNames[POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS] = "reattributed"; 126 } 127 128 /** 129 * Identifiers of models used for power estimation. 130 * 131 * @hide 132 */ 133 @IntDef(prefix = {"POWER_MODEL_"}, value = { 134 POWER_MODEL_UNDEFINED, 135 POWER_MODEL_POWER_PROFILE, 136 POWER_MODEL_ENERGY_CONSUMPTION, 137 }) 138 @Retention(RetentionPolicy.SOURCE) 139 public @interface PowerModel { 140 } 141 142 /** 143 * Unspecified power model. 144 */ 145 public static final int POWER_MODEL_UNDEFINED = 0; 146 147 /** 148 * Power model that is based on average consumption rates that hardware components 149 * consume in various states. 150 */ 151 public static final int POWER_MODEL_POWER_PROFILE = 1; 152 153 /** 154 * Power model that is based on energy consumption stats provided by PowerStats HAL. 155 */ 156 public static final int POWER_MODEL_ENERGY_CONSUMPTION = 2; 157 158 /** 159 * Identifiers of consumed power aggregations. 160 * 161 * @hide 162 */ 163 @IntDef(prefix = {"PROCESS_STATE_"}, value = { 164 PROCESS_STATE_ANY, 165 PROCESS_STATE_UNSPECIFIED, 166 PROCESS_STATE_FOREGROUND, 167 PROCESS_STATE_BACKGROUND, 168 PROCESS_STATE_FOREGROUND_SERVICE, 169 PROCESS_STATE_CACHED, 170 }) 171 @Retention(RetentionPolicy.SOURCE) 172 public @interface ProcessState { 173 } 174 175 public static final int PROCESS_STATE_UNSPECIFIED = 0; 176 public static final int PROCESS_STATE_ANY = PROCESS_STATE_UNSPECIFIED; 177 public static final int PROCESS_STATE_FOREGROUND = 1; 178 public static final int PROCESS_STATE_BACKGROUND = 2; 179 public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3; 180 public static final int PROCESS_STATE_CACHED = 4; 181 182 public static final int PROCESS_STATE_COUNT = 5; 183 184 private static final String[] sProcessStateNames = new String[PROCESS_STATE_COUNT]; 185 186 static { 187 // Assign individually to avoid future mismatch 188 sProcessStateNames[PROCESS_STATE_UNSPECIFIED] = "unspecified"; 189 sProcessStateNames[PROCESS_STATE_FOREGROUND] = "fg"; 190 sProcessStateNames[PROCESS_STATE_BACKGROUND] = "bg"; 191 sProcessStateNames[PROCESS_STATE_FOREGROUND_SERVICE] = "fgs"; 192 sProcessStateNames[PROCESS_STATE_CACHED] = "cached"; 193 } 194 195 private static final int[] SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = { 196 POWER_COMPONENT_CPU, 197 POWER_COMPONENT_MOBILE_RADIO, 198 POWER_COMPONENT_WIFI, 199 POWER_COMPONENT_BLUETOOTH, 200 POWER_COMPONENT_AUDIO, 201 POWER_COMPONENT_VIDEO, 202 POWER_COMPONENT_FLASHLIGHT, 203 POWER_COMPONENT_CAMERA, 204 POWER_COMPONENT_GNSS, 205 }; 206 207 static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0; 208 static final int COLUMN_COUNT = 1; 209 210 /** 211 * Identifies power attribution dimensions that a caller is interested in. 212 */ 213 public static final class Dimensions { 214 public final @PowerComponent int powerComponent; 215 public final @ProcessState int processState; 216 Dimensions(int powerComponent, int processState)217 public Dimensions(int powerComponent, int processState) { 218 this.powerComponent = powerComponent; 219 this.processState = processState; 220 } 221 222 @Override toString()223 public String toString() { 224 boolean dimensionSpecified = false; 225 StringBuilder sb = new StringBuilder(); 226 if (powerComponent != POWER_COMPONENT_ANY) { 227 sb.append("powerComponent=").append(sPowerComponentNames[powerComponent]); 228 dimensionSpecified = true; 229 } 230 if (processState != PROCESS_STATE_UNSPECIFIED) { 231 if (dimensionSpecified) { 232 sb.append(", "); 233 } 234 sb.append("processState=").append(sProcessStateNames[processState]); 235 dimensionSpecified = true; 236 } 237 if (!dimensionSpecified) { 238 sb.append("any components and process states"); 239 } 240 return sb.toString(); 241 } 242 } 243 244 public static final Dimensions UNSPECIFIED_DIMENSIONS = 245 new Dimensions(POWER_COMPONENT_ANY, PROCESS_STATE_ANY); 246 247 /** 248 * Identifies power attribution dimensions that are captured by a data element of 249 * a BatteryConsumer. These Keys are used to access those values and to set them using 250 * Builders. See for example {@link #getConsumedPower(Key)}. 251 * 252 * Keys cannot be allocated by the client - they can only be obtained by calling 253 * {@link #getKeys} or {@link #getKey}. All BatteryConsumers that are part of the 254 * same BatteryUsageStats share the same set of keys, therefore it is safe to obtain 255 * the keys from one BatteryConsumer and apply them to other BatteryConsumers 256 * in the same BatteryUsageStats. 257 */ 258 public static final class Key { 259 public final @PowerComponent int powerComponent; 260 public final @ProcessState int processState; 261 262 final int mPowerModelColumnIndex; 263 final int mPowerColumnIndex; 264 final int mDurationColumnIndex; 265 private String mShortString; 266 Key(int powerComponent, int processState, int powerModelColumnIndex, int powerColumnIndex, int durationColumnIndex)267 private Key(int powerComponent, int processState, int powerModelColumnIndex, 268 int powerColumnIndex, int durationColumnIndex) { 269 this.powerComponent = powerComponent; 270 this.processState = processState; 271 272 mPowerModelColumnIndex = powerModelColumnIndex; 273 mPowerColumnIndex = powerColumnIndex; 274 mDurationColumnIndex = durationColumnIndex; 275 } 276 277 @SuppressWarnings("EqualsUnsafeCast") 278 @Override equals(Object o)279 public boolean equals(Object o) { 280 // Skipping null and class check for performance 281 final Key key = (Key) o; 282 return powerComponent == key.powerComponent 283 && processState == key.processState; 284 } 285 286 @Override hashCode()287 public int hashCode() { 288 int result = powerComponent; 289 result = 31 * result + processState; 290 return result; 291 } 292 293 /** 294 * Returns a string suitable for use in dumpsys. 295 */ toShortString()296 public String toShortString() { 297 if (mShortString == null) { 298 StringBuilder sb = new StringBuilder(); 299 sb.append(powerComponentIdToString(powerComponent)); 300 if (processState != PROCESS_STATE_UNSPECIFIED) { 301 sb.append(':'); 302 sb.append(processStateToString(processState)); 303 } 304 mShortString = sb.toString(); 305 } 306 return mShortString; 307 } 308 } 309 310 protected final BatteryConsumerData mData; 311 protected final PowerComponents mPowerComponents; 312 BatteryConsumer(BatteryConsumerData data, @NonNull PowerComponents powerComponents)313 protected BatteryConsumer(BatteryConsumerData data, @NonNull PowerComponents powerComponents) { 314 mData = data; 315 mPowerComponents = powerComponents; 316 } 317 BatteryConsumer(BatteryConsumerData data)318 public BatteryConsumer(BatteryConsumerData data) { 319 mData = data; 320 mPowerComponents = new PowerComponents(data); 321 } 322 323 /** 324 * Total power consumed by this consumer, in mAh. 325 */ getConsumedPower()326 public double getConsumedPower() { 327 return mPowerComponents.getConsumedPower(UNSPECIFIED_DIMENSIONS); 328 } 329 330 /** 331 * Returns power consumed aggregated over the specified dimensions, in mAh. 332 */ getConsumedPower(Dimensions dimensions)333 public double getConsumedPower(Dimensions dimensions) { 334 return mPowerComponents.getConsumedPower(dimensions); 335 } 336 337 /** 338 * Returns keys for various power values attributed to the specified component 339 * held by this BatteryUsageStats object. 340 */ getKeys(@owerComponent int componentId)341 public Key[] getKeys(@PowerComponent int componentId) { 342 return mData.getKeys(componentId); 343 } 344 345 /** 346 * Returns the key for the power attributed to the specified component, 347 * for all values of other dimensions such as process state. 348 */ getKey(@owerComponent int componentId)349 public Key getKey(@PowerComponent int componentId) { 350 return mData.getKey(componentId, PROCESS_STATE_UNSPECIFIED); 351 } 352 353 /** 354 * Returns the key for the power attributed to the specified component and process state. 355 */ getKey(@owerComponent int componentId, @ProcessState int processState)356 public Key getKey(@PowerComponent int componentId, @ProcessState int processState) { 357 return mData.getKey(componentId, processState); 358 } 359 360 /** 361 * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. 362 * 363 * @param componentId The ID of the power component, e.g. 364 * {@link BatteryConsumer#POWER_COMPONENT_CPU}. 365 * @return Amount of consumed power in mAh. 366 */ getConsumedPower(@owerComponent int componentId)367 public double getConsumedPower(@PowerComponent int componentId) { 368 return mPowerComponents.getConsumedPower( 369 mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED)); 370 } 371 372 /** 373 * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. 374 * 375 * @param key The key of the power component, obtained by calling {@link #getKey} or 376 * {@link #getKeys} method. 377 * @return Amount of consumed power in mAh. 378 */ getConsumedPower(@onNull Key key)379 public double getConsumedPower(@NonNull Key key) { 380 return mPowerComponents.getConsumedPower(key); 381 } 382 383 /** 384 * Returns the ID of the model that was used for power estimation. 385 * 386 * @param componentId The ID of the power component, e.g. 387 * {@link BatteryConsumer#POWER_COMPONENT_CPU}. 388 */ getPowerModel(@atteryConsumer.PowerComponent int componentId)389 public @PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int componentId) { 390 return mPowerComponents.getPowerModel( 391 mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED)); 392 } 393 394 /** 395 * Returns the ID of the model that was used for power estimation. 396 * 397 * @param key The key of the power component, obtained by calling {@link #getKey} or 398 * {@link #getKeys} method. 399 */ getPowerModel(@onNull BatteryConsumer.Key key)400 public @PowerModel int getPowerModel(@NonNull BatteryConsumer.Key key) { 401 return mPowerComponents.getPowerModel(key); 402 } 403 404 /** 405 * Returns the amount of drain attributed to the specified custom drain type. 406 * 407 * @param componentId The ID of the custom power component. 408 * @return Amount of consumed power in mAh. 409 */ getConsumedPowerForCustomComponent(int componentId)410 public double getConsumedPowerForCustomComponent(int componentId) { 411 return mPowerComponents.getConsumedPowerForCustomComponent(componentId); 412 } 413 getCustomPowerComponentCount()414 public int getCustomPowerComponentCount() { 415 return mData.layout.customPowerComponentCount; 416 } 417 418 /** 419 * Returns the name of the specified power component. 420 * 421 * @param componentId The ID of the custom power component. 422 */ getCustomPowerComponentName(int componentId)423 public String getCustomPowerComponentName(int componentId) { 424 return mPowerComponents.getCustomPowerComponentName(componentId); 425 } 426 427 /** 428 * Returns the amount of time since BatteryStats reset used by the specified component, e.g. 429 * CPU, WiFi etc. 430 * 431 * @param componentId The ID of the power component, e.g. 432 * {@link UidBatteryConsumer#POWER_COMPONENT_CPU}. 433 * @return Amount of time in milliseconds. 434 */ getUsageDurationMillis(@owerComponent int componentId)435 public long getUsageDurationMillis(@PowerComponent int componentId) { 436 return mPowerComponents.getUsageDurationMillis(getKey(componentId)); 437 } 438 439 /** 440 * Returns the amount of time since BatteryStats reset used by the specified component, e.g. 441 * CPU, WiFi etc. 442 * 443 * 444 * @param key The key of the power component, obtained by calling {@link #getKey} or 445 * {@link #getKeys} method. 446 * @return Amount of time in milliseconds. 447 */ getUsageDurationMillis(@onNull Key key)448 public long getUsageDurationMillis(@NonNull Key key) { 449 return mPowerComponents.getUsageDurationMillis(key); 450 } 451 452 /** 453 * Returns the amount of usage time attributed to the specified custom component 454 * since BatteryStats reset. 455 * 456 * @param componentId The ID of the custom power component. 457 * @return Amount of time in milliseconds. 458 */ getUsageDurationForCustomComponentMillis(int componentId)459 public long getUsageDurationForCustomComponentMillis(int componentId) { 460 return mPowerComponents.getUsageDurationForCustomComponentMillis(componentId); 461 } 462 463 /** 464 * Returns the name of the specified component. Intended for logging and debugging. 465 */ powerComponentIdToString(@atteryConsumer.PowerComponent int componentId)466 public static String powerComponentIdToString(@BatteryConsumer.PowerComponent int componentId) { 467 if (componentId == POWER_COMPONENT_ANY) { 468 return "all"; 469 } 470 return sPowerComponentNames[componentId]; 471 } 472 473 /** 474 * Returns the name of the specified power model. Intended for logging and debugging. 475 */ powerModelToString(@atteryConsumer.PowerModel int powerModel)476 public static String powerModelToString(@BatteryConsumer.PowerModel int powerModel) { 477 switch (powerModel) { 478 case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION: 479 return "energy consumption"; 480 case BatteryConsumer.POWER_MODEL_POWER_PROFILE: 481 return "power profile"; 482 default: 483 return ""; 484 } 485 } 486 487 /** 488 * Returns the equivalent PowerModel enum for the specified power model. 489 * {@see BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage.PowerModel} 490 */ powerModelToProtoEnum(@atteryConsumer.PowerModel int powerModel)491 public static int powerModelToProtoEnum(@BatteryConsumer.PowerModel int powerModel) { 492 switch (powerModel) { 493 case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION: 494 return BatteryUsageStatsAtomsProto.PowerComponentModel.MEASURED_ENERGY; 495 case BatteryConsumer.POWER_MODEL_POWER_PROFILE: 496 return BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_PROFILE; 497 default: 498 return BatteryUsageStatsAtomsProto.PowerComponentModel.UNDEFINED; 499 } 500 } 501 502 /** 503 * Returns the name of the specified process state. Intended for logging and debugging. 504 */ processStateToString(@atteryConsumer.ProcessState int processState)505 public static String processStateToString(@BatteryConsumer.ProcessState int processState) { 506 return sProcessStateNames[processState]; 507 } 508 509 /** 510 * Prints the stats in a human-readable format. 511 */ dump(PrintWriter pw)512 public void dump(PrintWriter pw) { 513 dump(pw, true); 514 } 515 516 /** 517 * Prints the stats in a human-readable format. 518 * 519 * @param skipEmptyComponents if true, omit any power components with a zero amount. 520 */ dump(PrintWriter pw, boolean skipEmptyComponents)521 public abstract void dump(PrintWriter pw, boolean skipEmptyComponents); 522 523 /** Returns whether there are any atoms.proto BATTERY_CONSUMER_DATA data to write to a proto. */ hasStatsProtoData()524 boolean hasStatsProtoData() { 525 return writeStatsProtoImpl(null, /* Irrelevant fieldId: */ 0); 526 } 527 528 /** Writes the atoms.proto BATTERY_CONSUMER_DATA for this BatteryConsumer to the given proto. */ writeStatsProto(@onNull ProtoOutputStream proto, long fieldId)529 void writeStatsProto(@NonNull ProtoOutputStream proto, long fieldId) { 530 writeStatsProtoImpl(proto, fieldId); 531 } 532 533 /** 534 * Returns whether there are any atoms.proto BATTERY_CONSUMER_DATA data to write to a proto, 535 * and writes it to the given proto if it is non-null. 536 */ writeStatsProtoImpl(@ullable ProtoOutputStream proto, long fieldId)537 private boolean writeStatsProtoImpl(@Nullable ProtoOutputStream proto, long fieldId) { 538 final long totalConsumedPowerDeciCoulombs = convertMahToDeciCoulombs(getConsumedPower()); 539 540 if (totalConsumedPowerDeciCoulombs == 0) { 541 // NOTE: Strictly speaking we should also check !mPowerComponents.hasStatsProtoData(). 542 // However, that call is a bit expensive (a for loop). And the only way that 543 // totalConsumedPower can be 0 while mPowerComponents.hasStatsProtoData() is true is 544 // if POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS (which is the only negative 545 // allowed) happens to exactly equal the sum of all other components, which 546 // can't really happen in practice. 547 // So we'll just adopt the rule "if total==0, don't write any details". 548 // If negative values are used for other things in the future, this can be revisited. 549 return false; 550 } 551 if (proto == null) { 552 // We're just asked whether there is data, not to actually write it. And there is. 553 return true; 554 } 555 556 final long token = proto.start(fieldId); 557 proto.write( 558 BatteryUsageStatsAtomsProto.BatteryConsumerData.TOTAL_CONSUMED_POWER_DECI_COULOMBS, 559 totalConsumedPowerDeciCoulombs); 560 mPowerComponents.writeStatsProto(proto); 561 proto.end(token); 562 563 return true; 564 } 565 566 /** Converts charge from milliamp hours (mAh) to decicoulombs (dC). */ convertMahToDeciCoulombs(double powerMah)567 static long convertMahToDeciCoulombs(double powerMah) { 568 return (long) (powerMah * (10 * 3600 / 1000) + 0.5); 569 } 570 571 static class BatteryConsumerData { 572 private final CursorWindow mCursorWindow; 573 private final int mCursorRow; 574 public final BatteryConsumerDataLayout layout; 575 BatteryConsumerData(CursorWindow cursorWindow, int cursorRow, BatteryConsumerDataLayout layout)576 BatteryConsumerData(CursorWindow cursorWindow, int cursorRow, 577 BatteryConsumerDataLayout layout) { 578 mCursorWindow = cursorWindow; 579 mCursorRow = cursorRow; 580 this.layout = layout; 581 } 582 583 @Nullable create(CursorWindow cursorWindow, BatteryConsumerDataLayout layout)584 static BatteryConsumerData create(CursorWindow cursorWindow, 585 BatteryConsumerDataLayout layout) { 586 int cursorRow = cursorWindow.getNumRows(); 587 if (!cursorWindow.allocRow()) { 588 Slog.e(TAG, "Cannot allocate BatteryConsumerData: too many UIDs: " + cursorRow); 589 cursorRow = -1; 590 } 591 return new BatteryConsumerData(cursorWindow, cursorRow, layout); 592 } 593 getKeys(int componentId)594 public Key[] getKeys(int componentId) { 595 return layout.keys[componentId]; 596 } 597 getKeyOrThrow(int componentId, int processState)598 Key getKeyOrThrow(int componentId, int processState) { 599 Key key = getKey(componentId, processState); 600 if (key == null) { 601 if (processState == PROCESS_STATE_ANY) { 602 throw new IllegalArgumentException( 603 "Unsupported power component ID: " + componentId); 604 } else { 605 throw new IllegalArgumentException( 606 "Unsupported power component ID: " + componentId 607 + " process state: " + processState); 608 } 609 } 610 return key; 611 } 612 getKey(int componentId, int processState)613 Key getKey(int componentId, int processState) { 614 if (componentId >= POWER_COMPONENT_COUNT) { 615 return null; 616 } 617 618 if (processState == PROCESS_STATE_ANY) { 619 // The 0-th key for each component corresponds to the roll-up, 620 // across all dimensions. We might as well skip the iteration over the array. 621 return layout.keys[componentId][0]; 622 } else { 623 for (Key key : layout.keys[componentId]) { 624 if (key.processState == processState) { 625 return key; 626 } 627 } 628 } 629 return null; 630 } 631 putInt(int columnIndex, int value)632 void putInt(int columnIndex, int value) { 633 if (mCursorRow == -1) { 634 return; 635 } 636 mCursorWindow.putLong(value, mCursorRow, columnIndex); 637 } 638 getInt(int columnIndex)639 int getInt(int columnIndex) { 640 if (mCursorRow == -1) { 641 return 0; 642 } 643 return mCursorWindow.getInt(mCursorRow, columnIndex); 644 } 645 putDouble(int columnIndex, double value)646 void putDouble(int columnIndex, double value) { 647 if (mCursorRow == -1) { 648 return; 649 } 650 mCursorWindow.putDouble(value, mCursorRow, columnIndex); 651 } 652 getDouble(int columnIndex)653 double getDouble(int columnIndex) { 654 if (mCursorRow == -1) { 655 return 0; 656 } 657 return mCursorWindow.getDouble(mCursorRow, columnIndex); 658 } 659 putLong(int columnIndex, long value)660 void putLong(int columnIndex, long value) { 661 if (mCursorRow == -1) { 662 return; 663 } 664 mCursorWindow.putLong(value, mCursorRow, columnIndex); 665 } 666 getLong(int columnIndex)667 long getLong(int columnIndex) { 668 if (mCursorRow == -1) { 669 return 0; 670 } 671 return mCursorWindow.getLong(mCursorRow, columnIndex); 672 } 673 putString(int columnIndex, String value)674 void putString(int columnIndex, String value) { 675 if (mCursorRow == -1) { 676 return; 677 } 678 mCursorWindow.putString(value, mCursorRow, columnIndex); 679 } 680 getString(int columnIndex)681 String getString(int columnIndex) { 682 if (mCursorRow == -1) { 683 return null; 684 } 685 return mCursorWindow.getString(mCursorRow, columnIndex); 686 } 687 } 688 689 static class BatteryConsumerDataLayout { 690 private static final Key[] KEY_ARRAY = new Key[0]; 691 public static final int POWER_MODEL_NOT_INCLUDED = -1; 692 public final String[] customPowerComponentNames; 693 public final int customPowerComponentCount; 694 public final boolean powerModelsIncluded; 695 public final boolean processStateDataIncluded; 696 public final Key[][] keys; 697 public final int totalConsumedPowerColumnIndex; 698 public final int firstCustomConsumedPowerColumn; 699 public final int firstCustomUsageDurationColumn; 700 public final int columnCount; 701 public final Key[][] processStateKeys; 702 BatteryConsumerDataLayout(int firstColumn, String[] customPowerComponentNames, boolean powerModelsIncluded, boolean includeProcessStateData)703 private BatteryConsumerDataLayout(int firstColumn, String[] customPowerComponentNames, 704 boolean powerModelsIncluded, boolean includeProcessStateData) { 705 this.customPowerComponentNames = customPowerComponentNames; 706 this.customPowerComponentCount = customPowerComponentNames.length; 707 this.powerModelsIncluded = powerModelsIncluded; 708 this.processStateDataIncluded = includeProcessStateData; 709 710 int columnIndex = firstColumn; 711 712 totalConsumedPowerColumnIndex = columnIndex++; 713 714 keys = new Key[POWER_COMPONENT_COUNT][]; 715 716 ArrayList<Key> perComponentKeys = new ArrayList<>(); 717 for (int componentId = 0; componentId < POWER_COMPONENT_COUNT; componentId++) { 718 perComponentKeys.clear(); 719 720 // Declare the Key for the power component, ignoring other dimensions. 721 perComponentKeys.add( 722 new Key(componentId, PROCESS_STATE_ANY, 723 powerModelsIncluded 724 ? columnIndex++ 725 : POWER_MODEL_NOT_INCLUDED, // power model 726 columnIndex++, // power 727 columnIndex++ // usage duration 728 )); 729 730 // Declare Keys for all process states, if needed 731 if (includeProcessStateData) { 732 boolean isSupported = false; 733 for (int id : SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE) { 734 if (id == componentId) { 735 isSupported = true; 736 break; 737 } 738 } 739 if (isSupported) { 740 for (int processState = 0; processState < PROCESS_STATE_COUNT; 741 processState++) { 742 if (processState == PROCESS_STATE_UNSPECIFIED) { 743 continue; 744 } 745 746 perComponentKeys.add( 747 new Key(componentId, processState, 748 powerModelsIncluded 749 ? columnIndex++ 750 : POWER_MODEL_NOT_INCLUDED, // power model 751 columnIndex++, // power 752 columnIndex++ // usage duration 753 )); 754 } 755 } 756 } 757 758 keys[componentId] = perComponentKeys.toArray(KEY_ARRAY); 759 } 760 761 if (includeProcessStateData) { 762 processStateKeys = new Key[BatteryConsumer.PROCESS_STATE_COUNT][]; 763 ArrayList<Key> perProcStateKeys = new ArrayList<>(); 764 for (int processState = 0; processState < PROCESS_STATE_COUNT; processState++) { 765 if (processState == PROCESS_STATE_UNSPECIFIED) { 766 continue; 767 } 768 769 perProcStateKeys.clear(); 770 for (int i = 0; i < keys.length; i++) { 771 for (int j = 0; j < keys[i].length; j++) { 772 if (keys[i][j].processState == processState) { 773 perProcStateKeys.add(keys[i][j]); 774 } 775 } 776 } 777 processStateKeys[processState] = perProcStateKeys.toArray(KEY_ARRAY); 778 } 779 } else { 780 processStateKeys = null; 781 } 782 783 firstCustomConsumedPowerColumn = columnIndex; 784 columnIndex += customPowerComponentCount; 785 786 firstCustomUsageDurationColumn = columnIndex; 787 columnIndex += customPowerComponentCount; 788 789 columnCount = columnIndex; 790 } 791 } 792 createBatteryConsumerDataLayout( String[] customPowerComponentNames, boolean includePowerModels, boolean includeProcessStateData)793 static BatteryConsumerDataLayout createBatteryConsumerDataLayout( 794 String[] customPowerComponentNames, boolean includePowerModels, 795 boolean includeProcessStateData) { 796 int columnCount = BatteryConsumer.COLUMN_COUNT; 797 columnCount = Math.max(columnCount, AggregateBatteryConsumer.COLUMN_COUNT); 798 columnCount = Math.max(columnCount, UidBatteryConsumer.COLUMN_COUNT); 799 columnCount = Math.max(columnCount, UserBatteryConsumer.COLUMN_COUNT); 800 801 return new BatteryConsumerDataLayout(columnCount, customPowerComponentNames, 802 includePowerModels, includeProcessStateData); 803 } 804 805 protected abstract static class BaseBuilder<T extends BaseBuilder<?>> { 806 protected final BatteryConsumer.BatteryConsumerData mData; 807 protected final PowerComponents.Builder mPowerComponentsBuilder; 808 BaseBuilder(BatteryConsumer.BatteryConsumerData data, int consumerType, double minConsumedPowerThreshold)809 public BaseBuilder(BatteryConsumer.BatteryConsumerData data, int consumerType, 810 double minConsumedPowerThreshold) { 811 mData = data; 812 data.putLong(COLUMN_INDEX_BATTERY_CONSUMER_TYPE, consumerType); 813 814 mPowerComponentsBuilder = new PowerComponents.Builder(data, minConsumedPowerThreshold); 815 } 816 817 @Nullable getKeys(@owerComponent int componentId)818 public Key[] getKeys(@PowerComponent int componentId) { 819 return mData.getKeys(componentId); 820 } 821 822 @Nullable getKey(@owerComponent int componentId, @ProcessState int processState)823 public Key getKey(@PowerComponent int componentId, @ProcessState int processState) { 824 return mData.getKey(componentId, processState); 825 } 826 827 /** 828 * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. 829 * 830 * @param componentId The ID of the power component, e.g. 831 * {@link BatteryConsumer#POWER_COMPONENT_CPU}. 832 * @param componentPower Amount of consumed power in mAh. 833 */ 834 @NonNull setConsumedPower(@owerComponent int componentId, double componentPower)835 public T setConsumedPower(@PowerComponent int componentId, double componentPower) { 836 return setConsumedPower(componentId, componentPower, POWER_MODEL_POWER_PROFILE); 837 } 838 839 /** 840 * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. 841 * 842 * @param componentId The ID of the power component, e.g. 843 * {@link BatteryConsumer#POWER_COMPONENT_CPU}. 844 * @param componentPower Amount of consumed power in mAh. 845 */ 846 @SuppressWarnings("unchecked") 847 @NonNull setConsumedPower(@owerComponent int componentId, double componentPower, @PowerModel int powerModel)848 public T setConsumedPower(@PowerComponent int componentId, double componentPower, 849 @PowerModel int powerModel) { 850 mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED), 851 componentPower, powerModel); 852 return (T) this; 853 } 854 855 @SuppressWarnings("unchecked") 856 @NonNull addConsumedPower(@owerComponent int componentId, double componentPower, @PowerModel int powerModel)857 public T addConsumedPower(@PowerComponent int componentId, double componentPower, 858 @PowerModel int powerModel) { 859 mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED), 860 componentPower, powerModel); 861 return (T) this; 862 } 863 864 @SuppressWarnings("unchecked") 865 @NonNull setConsumedPower(Key key, double componentPower, @PowerModel int powerModel)866 public T setConsumedPower(Key key, double componentPower, @PowerModel int powerModel) { 867 mPowerComponentsBuilder.setConsumedPower(key, componentPower, powerModel); 868 return (T) this; 869 } 870 871 @SuppressWarnings("unchecked") 872 @NonNull addConsumedPower(Key key, double componentPower, @PowerModel int powerModel)873 public T addConsumedPower(Key key, double componentPower, @PowerModel int powerModel) { 874 mPowerComponentsBuilder.addConsumedPower(key, componentPower, powerModel); 875 return (T) this; 876 } 877 878 /** 879 * Sets the amount of drain attributed to the specified custom drain type. 880 * 881 * @param componentId The ID of the custom power component. 882 * @param componentPower Amount of consumed power in mAh. 883 */ 884 @SuppressWarnings("unchecked") 885 @NonNull setConsumedPowerForCustomComponent(int componentId, double componentPower)886 public T setConsumedPowerForCustomComponent(int componentId, double componentPower) { 887 mPowerComponentsBuilder.setConsumedPowerForCustomComponent(componentId, componentPower); 888 return (T) this; 889 } 890 891 /** 892 * Sets the amount of time used by the specified component, e.g. CPU, WiFi etc. 893 * 894 * @param componentId The ID of the power component, e.g. 895 * {@link UidBatteryConsumer#POWER_COMPONENT_CPU}. 896 * @param componentUsageTimeMillis Amount of time in microseconds. 897 */ 898 @SuppressWarnings("unchecked") 899 @NonNull setUsageDurationMillis(@idBatteryConsumer.PowerComponent int componentId, long componentUsageTimeMillis)900 public T setUsageDurationMillis(@UidBatteryConsumer.PowerComponent int componentId, 901 long componentUsageTimeMillis) { 902 mPowerComponentsBuilder 903 .setUsageDurationMillis(getKey(componentId, PROCESS_STATE_UNSPECIFIED), 904 componentUsageTimeMillis); 905 return (T) this; 906 } 907 908 909 @SuppressWarnings("unchecked") 910 @NonNull setUsageDurationMillis(Key key, long componentUsageTimeMillis)911 public T setUsageDurationMillis(Key key, long componentUsageTimeMillis) { 912 mPowerComponentsBuilder.setUsageDurationMillis(key, componentUsageTimeMillis); 913 return (T) this; 914 } 915 916 /** 917 * Sets the amount of time used by the specified custom component. 918 * 919 * @param componentId The ID of the custom power component. 920 * @param componentUsageTimeMillis Amount of time in microseconds. 921 */ 922 @SuppressWarnings("unchecked") 923 @NonNull setUsageDurationForCustomComponentMillis(int componentId, long componentUsageTimeMillis)924 public T setUsageDurationForCustomComponentMillis(int componentId, 925 long componentUsageTimeMillis) { 926 mPowerComponentsBuilder.setUsageDurationForCustomComponentMillis(componentId, 927 componentUsageTimeMillis); 928 return (T) this; 929 } 930 931 /** 932 * Returns the total power accumulated by this builder so far. It may change 933 * by the time the {@code build()} method is called. 934 */ getTotalPower()935 public double getTotalPower() { 936 return mPowerComponentsBuilder.getTotalPower(); 937 } 938 } 939 } 940