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 package android.os; 17 18 import static android.os.BatteryConsumer.convertMahToDeciCoulombs; 19 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.util.TypedXmlPullParser; 23 import android.util.TypedXmlSerializer; 24 import android.util.proto.ProtoOutputStream; 25 26 import com.android.internal.os.PowerCalculator; 27 28 import org.xmlpull.v1.XmlPullParser; 29 import org.xmlpull.v1.XmlPullParserException; 30 31 import java.io.IOException; 32 import java.io.PrintWriter; 33 import java.util.Arrays; 34 35 /** 36 * Contains details of battery attribution data broken down to individual power drain types 37 * such as CPU, RAM, GPU etc. 38 * 39 * @hide 40 */ 41 class PowerComponents { 42 private static final int CUSTOM_POWER_COMPONENT_OFFSET = BatteryConsumer.POWER_COMPONENT_COUNT 43 - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID; 44 45 private final double mConsumedPowerMah; 46 @NonNull 47 private final double[] mPowerComponentsMah; 48 @NonNull 49 private final long[] mUsageDurationsMs; 50 private final int mCustomPowerComponentCount; 51 @Nullable 52 private final byte[] mPowerModels; 53 // Not written to Parcel and must be explicitly restored during the parent object's unparceling 54 private String[] mCustomPowerComponentNames; 55 PowerComponents(@onNull Builder builder)56 PowerComponents(@NonNull Builder builder) { 57 mCustomPowerComponentNames = builder.mCustomPowerComponentNames; 58 mCustomPowerComponentCount = mCustomPowerComponentNames.length; 59 mPowerComponentsMah = builder.mPowerComponentsMah; 60 mUsageDurationsMs = builder.mUsageDurationsMs; 61 mConsumedPowerMah = builder.getTotalPower(); 62 mPowerModels = builder.getPowerModels(); 63 } 64 PowerComponents(@onNull Parcel source)65 PowerComponents(@NonNull Parcel source) { 66 mConsumedPowerMah = source.readDouble(); 67 mCustomPowerComponentCount = source.readInt(); 68 mPowerComponentsMah = source.createDoubleArray(); 69 mUsageDurationsMs = source.createLongArray(); 70 if (source.readBoolean()) { 71 mPowerModels = new byte[BatteryConsumer.POWER_COMPONENT_COUNT]; 72 source.readByteArray(mPowerModels); 73 } else { 74 mPowerModels = null; 75 } 76 } 77 78 /** Writes contents to Parcel */ writeToParcel(@onNull Parcel dest, int flags)79 void writeToParcel(@NonNull Parcel dest, int flags) { 80 dest.writeDouble(mConsumedPowerMah); 81 dest.writeInt(mCustomPowerComponentCount); 82 dest.writeDoubleArray(mPowerComponentsMah); 83 dest.writeLongArray(mUsageDurationsMs); 84 if (mPowerModels != null) { 85 dest.writeBoolean(true); 86 dest.writeByteArray(mPowerModels); 87 } else { 88 dest.writeBoolean(false); 89 } 90 } 91 92 /** 93 * Total power consumed by this consumer, in mAh. 94 */ getConsumedPower()95 public double getConsumedPower() { 96 return mConsumedPowerMah; 97 } 98 99 /** 100 * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. 101 * 102 * @param componentId The ID of the power component, e.g. 103 * {@link BatteryConsumer#POWER_COMPONENT_CPU}. 104 * @return Amount of consumed power in mAh. 105 */ getConsumedPower(@atteryConsumer.PowerComponent int componentId)106 public double getConsumedPower(@BatteryConsumer.PowerComponent int componentId) { 107 if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) { 108 throw new IllegalArgumentException( 109 "Unsupported power component ID: " + componentId); 110 } 111 try { 112 return mPowerComponentsMah[componentId]; 113 } catch (ArrayIndexOutOfBoundsException e) { 114 throw new IllegalArgumentException("Unsupported power component ID: " + componentId); 115 } 116 } 117 118 /** 119 * Returns the amount of drain attributed to the specified custom drain type. 120 * 121 * @param componentId The ID of the custom power component. 122 * @return Amount of consumed power in mAh. 123 */ getConsumedPowerForCustomComponent(int componentId)124 public double getConsumedPowerForCustomComponent(int componentId) { 125 if (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID 126 && componentId < BatteryConsumer.LAST_CUSTOM_POWER_COMPONENT_ID) { 127 try { 128 return mPowerComponentsMah[CUSTOM_POWER_COMPONENT_OFFSET + componentId]; 129 } catch (ArrayIndexOutOfBoundsException e) { 130 throw new IllegalArgumentException( 131 "Unsupported custom power component ID: " + componentId); 132 } 133 } else { 134 throw new IllegalArgumentException( 135 "Unsupported custom power component ID: " + componentId); 136 } 137 } 138 setCustomPowerComponentNames(String[] customPowerComponentNames)139 void setCustomPowerComponentNames(String[] customPowerComponentNames) { 140 mCustomPowerComponentNames = customPowerComponentNames; 141 } 142 getCustomPowerComponentName(int componentId)143 public String getCustomPowerComponentName(int componentId) { 144 if (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID 145 && componentId < BatteryConsumer.LAST_CUSTOM_POWER_COMPONENT_ID) { 146 try { 147 return mCustomPowerComponentNames[componentId 148 - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID]; 149 } catch (ArrayIndexOutOfBoundsException e) { 150 throw new IllegalArgumentException( 151 "Unsupported custom power component ID: " + componentId); 152 } 153 } else { 154 throw new IllegalArgumentException( 155 "Unsupported custom power component ID: " + componentId); 156 } 157 } 158 hasPowerModels()159 public boolean hasPowerModels() { 160 return mPowerModels != null; 161 } 162 163 @BatteryConsumer.PowerModel getPowerModel(@atteryConsumer.PowerComponent int component)164 int getPowerModel(@BatteryConsumer.PowerComponent int component) { 165 if (!hasPowerModels()) { 166 throw new IllegalStateException( 167 "Power model IDs were not requested in the BatteryUsageStatsQuery"); 168 } 169 return mPowerModels[component]; 170 } 171 172 /** 173 * Returns the amount of time used by the specified component, e.g. CPU, WiFi etc. 174 * 175 * @param componentId The ID of the power component, e.g. 176 * {@link BatteryConsumer#POWER_COMPONENT_CPU}. 177 * @return Amount of time in milliseconds. 178 */ getUsageDurationMillis(@atteryConsumer.PowerComponent int componentId)179 public long getUsageDurationMillis(@BatteryConsumer.PowerComponent int componentId) { 180 if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) { 181 throw new IllegalArgumentException( 182 "Unsupported power component ID: " + componentId); 183 } 184 try { 185 return mUsageDurationsMs[componentId]; 186 } catch (ArrayIndexOutOfBoundsException e) { 187 throw new IllegalArgumentException("Unsupported power component ID: " + componentId); 188 } 189 } 190 191 /** 192 * Returns the amount of usage time attributed to the specified custom component. 193 * 194 * @param componentId The ID of the custom power component. 195 * @return Amount of time in milliseconds. 196 */ getUsageDurationForCustomComponentMillis(int componentId)197 public long getUsageDurationForCustomComponentMillis(int componentId) { 198 if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) { 199 throw new IllegalArgumentException( 200 "Unsupported custom power component ID: " + componentId); 201 } 202 try { 203 return mUsageDurationsMs[CUSTOM_POWER_COMPONENT_OFFSET + componentId]; 204 } catch (ArrayIndexOutOfBoundsException e) { 205 throw new IllegalArgumentException( 206 "Unsupported custom power component ID: " + componentId); 207 } 208 } 209 getCustomPowerComponentCount()210 public int getCustomPowerComponentCount() { 211 return mCustomPowerComponentCount; 212 } 213 214 /** 215 * Returns the largest usage duration among all power components. 216 */ getMaxComponentUsageDurationMillis()217 public long getMaxComponentUsageDurationMillis() { 218 long max = 0; 219 for (int i = mUsageDurationsMs.length - 1; i >= 0; i--) { 220 if (mUsageDurationsMs[i] > max) { 221 max = mUsageDurationsMs[i]; 222 } 223 } 224 return max; 225 } 226 dump(PrintWriter pw, boolean skipEmptyComponents)227 public void dump(PrintWriter pw, boolean skipEmptyComponents) { 228 String separator = ""; 229 for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT; 230 componentId++) { 231 final double componentPower = getConsumedPower(componentId); 232 if (skipEmptyComponents && componentPower == 0) { 233 continue; 234 } 235 pw.print(separator); separator = " "; 236 pw.print(BatteryConsumer.powerComponentIdToString(componentId)); 237 pw.print("="); 238 PowerCalculator.printPowerMah(pw, componentPower); 239 } 240 241 final int customComponentCount = getCustomPowerComponentCount(); 242 for (int customComponentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID; 243 customComponentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID 244 + customComponentCount; 245 customComponentId++) { 246 final double customComponentPower = 247 getConsumedPowerForCustomComponent(customComponentId); 248 if (skipEmptyComponents && customComponentPower == 0) { 249 continue; 250 } 251 pw.print(separator); separator = " "; 252 pw.print(getCustomPowerComponentName(customComponentId)); 253 pw.print("="); 254 PowerCalculator.printPowerMah(pw, customComponentPower); 255 } 256 } 257 258 /** Returns whether there are any atoms.proto POWER_COMPONENTS data to write to a proto. */ hasStatsProtoData()259 boolean hasStatsProtoData() { 260 return writeStatsProtoImpl(null); 261 } 262 263 /** Writes all atoms.proto POWER_COMPONENTS for this PowerComponents to the given proto. */ writeStatsProto(@onNull ProtoOutputStream proto)264 void writeStatsProto(@NonNull ProtoOutputStream proto) { 265 writeStatsProtoImpl(proto); 266 } 267 268 /** 269 * Returns whether there are any atoms.proto POWER_COMPONENTS data to write to a proto, 270 * and writes it to the given proto if it is non-null. 271 */ writeStatsProtoImpl(@ullable ProtoOutputStream proto)272 private boolean writeStatsProtoImpl(@Nullable ProtoOutputStream proto) { 273 boolean interestingData = false; 274 275 for (int idx = 0; idx < mPowerComponentsMah.length; idx++) { 276 final int componentId = idx < BatteryConsumer.POWER_COMPONENT_COUNT ? 277 idx : idx - CUSTOM_POWER_COMPONENT_OFFSET; 278 final long powerDeciCoulombs = convertMahToDeciCoulombs(mPowerComponentsMah[idx]); 279 final long durationMs = mUsageDurationsMs[idx]; 280 281 if (powerDeciCoulombs == 0 && durationMs == 0) { 282 // No interesting data. Make sure not to even write the COMPONENT int. 283 continue; 284 } 285 286 interestingData = true; 287 if (proto == null) { 288 // We're just asked whether there is data, not to actually write it. And there is. 289 return true; 290 } 291 292 final long token = 293 proto.start(BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS); 294 proto.write( 295 BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage 296 .COMPONENT, 297 componentId); 298 proto.write( 299 BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage 300 .POWER_DECI_COULOMBS, 301 powerDeciCoulombs); 302 proto.write( 303 BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage 304 .DURATION_MILLIS, 305 durationMs); 306 proto.end(token); 307 } 308 return interestingData; 309 } 310 311 void writeToXml(TypedXmlSerializer serializer) throws IOException { 312 serializer.startTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS); 313 for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT; 314 componentId++) { 315 final double powerMah = getConsumedPower(componentId); 316 final long durationMs = getUsageDurationMillis(componentId); 317 if (powerMah == 0 && durationMs == 0) { 318 continue; 319 } 320 321 serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT); 322 serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId); 323 if (powerMah != 0) { 324 serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah); 325 } 326 if (durationMs != 0) { 327 serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs); 328 } 329 if (mPowerModels != null) { 330 serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_MODEL, 331 mPowerModels[componentId]); 332 } 333 serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT); 334 } 335 336 final int customComponentEnd = 337 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + mCustomPowerComponentCount; 338 for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID; 339 componentId < customComponentEnd; 340 componentId++) { 341 final double powerMah = getConsumedPowerForCustomComponent(componentId); 342 final long durationMs = getUsageDurationForCustomComponentMillis(componentId); 343 if (powerMah == 0 && durationMs == 0) { 344 continue; 345 } 346 347 serializer.startTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT); 348 serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId); 349 if (powerMah != 0) { 350 serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah); 351 } 352 if (durationMs != 0) { 353 serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs); 354 } 355 serializer.endTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT); 356 } 357 358 serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS); 359 } 360 361 362 static void parseXml(TypedXmlPullParser parser, PowerComponents.Builder builder) 363 throws XmlPullParserException, IOException { 364 int eventType = parser.getEventType(); 365 if (eventType != XmlPullParser.START_TAG || !parser.getName().equals( 366 BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { 367 throw new XmlPullParserException("Invalid XML parser state"); 368 } 369 370 while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals( 371 BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) 372 && eventType != XmlPullParser.END_DOCUMENT) { 373 if (eventType == XmlPullParser.START_TAG) { 374 switch (parser.getName()) { 375 case BatteryUsageStats.XML_TAG_COMPONENT: { 376 int componentId = -1; 377 double powerMah = 0; 378 long durationMs = 0; 379 int model = BatteryConsumer.POWER_MODEL_UNDEFINED; 380 for (int i = 0; i < parser.getAttributeCount(); i++) { 381 switch (parser.getAttributeName(i)) { 382 case BatteryUsageStats.XML_ATTR_ID: 383 componentId = parser.getAttributeInt(i); 384 break; 385 case BatteryUsageStats.XML_ATTR_POWER: 386 powerMah = parser.getAttributeDouble(i); 387 break; 388 case BatteryUsageStats.XML_ATTR_DURATION: 389 durationMs = parser.getAttributeLong(i); 390 break; 391 case BatteryUsageStats.XML_ATTR_MODEL: 392 model = parser.getAttributeInt(i); 393 break; 394 } 395 } 396 builder.setConsumedPower(componentId, powerMah, model); 397 builder.setUsageDurationMillis(componentId, durationMs); 398 break; 399 } 400 case BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT: { 401 int componentId = -1; 402 double powerMah = 0; 403 long durationMs = 0; 404 for (int i = 0; i < parser.getAttributeCount(); i++) { 405 switch (parser.getAttributeName(i)) { 406 case BatteryUsageStats.XML_ATTR_ID: 407 componentId = parser.getAttributeInt(i); 408 break; 409 case BatteryUsageStats.XML_ATTR_POWER: 410 powerMah = parser.getAttributeDouble(i); 411 break; 412 case BatteryUsageStats.XML_ATTR_DURATION: 413 durationMs = parser.getAttributeLong(i); 414 break; 415 } 416 } 417 builder.setConsumedPowerForCustomComponent(componentId, powerMah); 418 builder.setUsageDurationForCustomComponentMillis(componentId, durationMs); 419 break; 420 } 421 } 422 } 423 eventType = parser.next(); 424 } 425 } 426 427 /** 428 * Builder for PowerComponents. 429 */ 430 static final class Builder { 431 private static final byte POWER_MODEL_UNINITIALIZED = -1; 432 433 private final double[] mPowerComponentsMah; 434 private final String[] mCustomPowerComponentNames; 435 private final long[] mUsageDurationsMs; 436 private final byte[] mPowerModels; 437 438 Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels) { 439 mCustomPowerComponentNames = customPowerComponentNames; 440 int powerComponentCount = 441 BatteryConsumer.POWER_COMPONENT_COUNT + mCustomPowerComponentNames.length; 442 mPowerComponentsMah = new double[powerComponentCount]; 443 mUsageDurationsMs = new long[powerComponentCount]; 444 if (includePowerModels) { 445 mPowerModels = new byte[BatteryConsumer.POWER_COMPONENT_COUNT]; 446 Arrays.fill(mPowerModels, POWER_MODEL_UNINITIALIZED); 447 } else { 448 mPowerModels = null; 449 } 450 } 451 452 /** 453 * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. 454 * 455 * @param componentId The ID of the power component, e.g. 456 * {@link BatteryConsumer#POWER_COMPONENT_CPU}. 457 * @param componentPower Amount of consumed power in mAh. 458 */ 459 @NonNull 460 public Builder setConsumedPower(@BatteryConsumer.PowerComponent int componentId, 461 double componentPower, @BatteryConsumer.PowerModel int powerModel) { 462 if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) { 463 throw new IllegalArgumentException( 464 "Unsupported power component ID: " + componentId); 465 } 466 try { 467 mPowerComponentsMah[componentId] = componentPower; 468 } catch (ArrayIndexOutOfBoundsException e) { 469 throw new IllegalArgumentException( 470 "Unsupported power component ID: " + componentId); 471 } 472 if (mPowerModels != null) { 473 mPowerModels[componentId] = (byte) powerModel; 474 } 475 return this; 476 } 477 478 /** 479 * Sets the amount of drain attributed to the specified custom drain type. 480 * 481 * @param componentId The ID of the custom power component. 482 * @param componentPower Amount of consumed power in mAh. 483 */ 484 @NonNull 485 public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) { 486 if (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID 487 && componentId < BatteryConsumer.LAST_CUSTOM_POWER_COMPONENT_ID) { 488 try { 489 mPowerComponentsMah[CUSTOM_POWER_COMPONENT_OFFSET + componentId] = 490 componentPower; 491 } catch (ArrayIndexOutOfBoundsException e) { 492 throw new IllegalArgumentException( 493 "Unsupported custom power component ID: " + componentId); 494 } 495 } else { 496 throw new IllegalArgumentException( 497 "Unsupported custom power component ID: " + componentId); 498 } 499 return this; 500 } 501 502 /** 503 * Sets the amount of time used by the specified component, e.g. CPU, WiFi etc. 504 * 505 * @param componentId The ID of the power component, e.g. 506 * {@link BatteryConsumer#POWER_COMPONENT_CPU}. 507 * @param componentUsageDurationMillis Amount of time in milliseconds. 508 */ 509 @NonNull 510 public Builder setUsageDurationMillis(@BatteryConsumer.PowerComponent int componentId, 511 long componentUsageDurationMillis) { 512 if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) { 513 throw new IllegalArgumentException( 514 "Unsupported power component ID: " + componentId); 515 } 516 try { 517 mUsageDurationsMs[componentId] = componentUsageDurationMillis; 518 } catch (ArrayIndexOutOfBoundsException e) { 519 throw new IllegalArgumentException( 520 "Unsupported power component ID: " + componentId); 521 } 522 return this; 523 } 524 525 /** 526 * Sets the amount of time used by the specified custom component. 527 * 528 * @param componentId The ID of the custom power component. 529 * @param componentUsageDurationMillis Amount of time in milliseconds. 530 */ 531 @NonNull 532 public Builder setUsageDurationForCustomComponentMillis(int componentId, 533 long componentUsageDurationMillis) { 534 if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) { 535 throw new IllegalArgumentException( 536 "Unsupported custom power component ID: " + componentId); 537 } 538 try { 539 mUsageDurationsMs[CUSTOM_POWER_COMPONENT_OFFSET + componentId] = 540 componentUsageDurationMillis; 541 } catch (ArrayIndexOutOfBoundsException e) { 542 throw new IllegalArgumentException( 543 "Unsupported custom power component ID: " + componentId); 544 } 545 return this; 546 } 547 548 public void addPowerAndDuration(PowerComponents.Builder other) { 549 addPowerAndDuration(other.mPowerComponentsMah, other.mUsageDurationsMs, 550 other.mPowerModels); 551 } 552 553 public void addPowerAndDuration(PowerComponents other) { 554 addPowerAndDuration(other.mPowerComponentsMah, other.mUsageDurationsMs, 555 other.mPowerModels); 556 } 557 558 private void addPowerAndDuration(double[] powerComponentsMah, 559 long[] usageDurationsMs, byte[] powerModels) { 560 if (mPowerComponentsMah.length != powerComponentsMah.length) { 561 throw new IllegalArgumentException( 562 "Number of power components does not match: " + powerComponentsMah.length 563 + ", expected: " + mPowerComponentsMah.length); 564 } 565 566 for (int i = mPowerComponentsMah.length - 1; i >= 0; i--) { 567 mPowerComponentsMah[i] += powerComponentsMah[i]; 568 } 569 for (int i = mUsageDurationsMs.length - 1; i >= 0; i--) { 570 mUsageDurationsMs[i] += usageDurationsMs[i]; 571 } 572 if (mPowerModels != null && powerModels != null) { 573 for (int i = mPowerModels.length - 1; i >= 0; i--) { 574 if (mPowerModels[i] == POWER_MODEL_UNINITIALIZED) { 575 mPowerModels[i] = powerModels[i]; 576 } else if (mPowerModels[i] != powerModels[i] 577 && powerModels[i] != POWER_MODEL_UNINITIALIZED) { 578 mPowerModels[i] = BatteryConsumer.POWER_MODEL_UNDEFINED; 579 } 580 } 581 } 582 } 583 584 /** 585 * Returns the total power accumulated by this builder so far. It may change 586 * by the time the {@code build()} method is called. 587 */ 588 public double getTotalPower() { 589 double totalPowerMah = 0; 590 for (int i = mPowerComponentsMah.length - 1; i >= 0; i--) { 591 totalPowerMah += mPowerComponentsMah[i]; 592 } 593 return totalPowerMah; 594 } 595 getPowerModels()596 private byte[] getPowerModels() { 597 if (mPowerModels == null) { 598 return null; 599 } 600 601 byte[] powerModels = new byte[mPowerModels.length]; 602 for (int i = mPowerModels.length - 1; i >= 0; i--) { 603 powerModels[i] = mPowerModels[i] != POWER_MODEL_UNINITIALIZED ? mPowerModels[i] 604 : BatteryConsumer.POWER_MODEL_UNDEFINED; 605 } 606 return powerModels; 607 } 608 609 /** 610 * Creates a read-only object out of the Builder values. 611 */ 612 @NonNull build()613 public PowerComponents build() { 614 return new PowerComponents(this); 615 } 616 } 617 } 618