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