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.POWER_COMPONENT_BASE; 20 import static android.os.BatteryConsumer.POWER_STATE_ANY; 21 import static android.os.BatteryConsumer.POWER_STATE_UNSPECIFIED; 22 import static android.os.BatteryConsumer.PROCESS_STATE_ANY; 23 import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED; 24 import static android.os.BatteryConsumer.SCREEN_STATE_ANY; 25 import static android.os.BatteryConsumer.SCREEN_STATE_UNSPECIFIED; 26 import static android.os.BatteryConsumer.convertMahToDeciCoulombs; 27 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.util.proto.ProtoOutputStream; 31 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.IOException; 39 import java.io.PrintWriter; 40 41 /** 42 * Contains details of battery attribution data broken down to individual power drain types 43 * such as CPU, RAM, GPU etc. 44 * 45 * @hide 46 */ 47 @android.ravenwood.annotation.RavenwoodKeepWholeClass 48 class PowerComponents { 49 private final BatteryConsumer.BatteryConsumerData mData; 50 PowerComponents(@onNull Builder builder)51 PowerComponents(@NonNull Builder builder) { 52 mData = builder.mData; 53 } 54 PowerComponents(BatteryConsumer.BatteryConsumerData data)55 PowerComponents(BatteryConsumer.BatteryConsumerData data) { 56 mData = data; 57 } 58 59 /** 60 * Total power consumed by this consumer, aggregated over the specified dimensions, in mAh. 61 */ getConsumedPower(@onNull BatteryConsumer.Dimensions dimensions)62 public double getConsumedPower(@NonNull BatteryConsumer.Dimensions dimensions) { 63 return getConsumedPower(dimensions.powerComponentId, dimensions.processState, 64 dimensions.screenState, dimensions.powerState); 65 } 66 67 /** 68 * Total power consumed by this consumer, aggregated over the specified dimensions, in mAh. 69 */ getConsumedPower(@atteryConsumer.PowerComponentId int powerComponent, @BatteryConsumer.ProcessState int processState, @BatteryConsumer.ScreenState int screenState, @BatteryConsumer.PowerState int powerState)70 public double getConsumedPower(@BatteryConsumer.PowerComponentId int powerComponent, 71 @BatteryConsumer.ProcessState int processState, 72 @BatteryConsumer.ScreenState int screenState, 73 @BatteryConsumer.PowerState int powerState) { 74 if (powerComponent == POWER_COMPONENT_ANY && processState == PROCESS_STATE_ANY 75 && screenState == SCREEN_STATE_ANY && powerState == POWER_STATE_ANY) { 76 return mData.getDouble(mData.layout.totalConsumedPowerColumnIndex); 77 } 78 79 if (!mData.layout.processStateDataIncluded && !(processState == PROCESS_STATE_UNSPECIFIED 80 || processState == PROCESS_STATE_ANY)) { 81 return 0; 82 } 83 84 BatteryConsumer.Key key = mData.layout.getKey(powerComponent, 85 mData.layout.processStateDataIncluded && processState != PROCESS_STATE_ANY 86 ? processState : PROCESS_STATE_UNSPECIFIED, 87 mData.layout.screenStateDataIncluded && screenState != SCREEN_STATE_ANY 88 ? screenState : SCREEN_STATE_UNSPECIFIED, 89 mData.layout.powerStateDataIncluded && powerState != POWER_STATE_ANY 90 ? powerState : POWER_STATE_UNSPECIFIED); 91 if (key != null && mData.hasValue(key.mPowerColumnIndex)) { 92 return mData.getDouble(key.mPowerColumnIndex); 93 } 94 95 double total = 0; 96 for (BatteryConsumer.Key k : mData.layout.keys) { 97 if (k.matches(powerComponent, processState, screenState, powerState)) { 98 total += mData.getDouble(k.mPowerColumnIndex); 99 } 100 } 101 return total; 102 } 103 104 /** 105 * Total usage duration by this consumer, aggregated over the specified dimensions, in ms. 106 */ getUsageDurationMillis(@onNull BatteryConsumer.Dimensions dimensions)107 public long getUsageDurationMillis(@NonNull BatteryConsumer.Dimensions dimensions) { 108 return getUsageDurationMillis(dimensions.powerComponentId, dimensions.processState, 109 dimensions.screenState, dimensions.powerState); 110 } 111 112 /** 113 * Total usage duration by this consumer, aggregated over the specified dimensions, in ms. 114 */ getUsageDurationMillis(@atteryConsumer.PowerComponentId int powerComponent, @BatteryConsumer.ProcessState int processState, @BatteryConsumer.ScreenState int screenState, @BatteryConsumer.PowerState int powerState)115 public long getUsageDurationMillis(@BatteryConsumer.PowerComponentId int powerComponent, 116 @BatteryConsumer.ProcessState int processState, 117 @BatteryConsumer.ScreenState int screenState, 118 @BatteryConsumer.PowerState int powerState) { 119 BatteryConsumer.Key key = mData.layout.getKey(powerComponent, 120 mData.layout.processStateDataIncluded && processState != PROCESS_STATE_ANY 121 ? processState : PROCESS_STATE_UNSPECIFIED, 122 mData.layout.screenStateDataIncluded && screenState != SCREEN_STATE_ANY 123 ? screenState : SCREEN_STATE_UNSPECIFIED, 124 mData.layout.powerStateDataIncluded && powerState != POWER_STATE_ANY 125 ? powerState : POWER_STATE_UNSPECIFIED); 126 if (key != null && mData.hasValue(key.mDurationColumnIndex)) { 127 return mData.getLong(key.mDurationColumnIndex); 128 } 129 130 long total = 0; 131 for (BatteryConsumer.Key k : mData.layout.keys) { 132 if (k.matches(powerComponent, processState, screenState, powerState)) { 133 total += mData.getLong(k.mDurationColumnIndex); 134 } 135 } 136 return total; 137 } 138 139 /** 140 * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. 141 * 142 * @param key The key of the power component, obtained by calling {@link BatteryConsumer#getKey} 143 * or {@link BatteryConsumer#getKeys} method. 144 * @return Amount of consumed power in mAh. 145 */ getConsumedPower(@onNull BatteryConsumer.Key key)146 public double getConsumedPower(@NonNull BatteryConsumer.Key key) { 147 if (mData.hasValue(key.mPowerColumnIndex)) { 148 return mData.getDouble(key.mPowerColumnIndex); 149 } 150 return getConsumedPower(key.powerComponentId, key.processState, key.screenState, 151 key.powerState); 152 } 153 getCustomPowerComponentName(int componentId)154 public String getCustomPowerComponentName(int componentId) { 155 return mData.layout.getPowerComponentName(componentId); 156 } 157 158 /** 159 * Returns the amount of time used by the specified component, e.g. CPU, WiFi etc. 160 * 161 * @param key The key of the power component, obtained by calling {@link BatteryConsumer#getKey} 162 * or {@link BatteryConsumer#getKeys} method. 163 * @return Amount of time in milliseconds. 164 */ getUsageDurationMillis(BatteryConsumer.Key key)165 public long getUsageDurationMillis(BatteryConsumer.Key key) { 166 if (mData.hasValue(key.mDurationColumnIndex)) { 167 return mData.getLong(key.mDurationColumnIndex); 168 } 169 170 return getUsageDurationMillis(key.powerComponentId, key.processState, key.screenState, 171 key.powerState); 172 } 173 dump(PrintWriter pw, @BatteryConsumer.ScreenState int screenState, @BatteryConsumer.PowerState int powerState, boolean skipEmptyComponents)174 void dump(PrintWriter pw, @BatteryConsumer.ScreenState int screenState, 175 @BatteryConsumer.PowerState int powerState, boolean skipEmptyComponents) { 176 StringBuilder sb = new StringBuilder(); 177 for (@BatteryConsumer.PowerComponentId int id : mData.layout.powerComponentIds) { 178 if (id == POWER_COMPONENT_BASE) { 179 continue; 180 } 181 182 dump(sb, id, PROCESS_STATE_ANY, screenState, powerState, skipEmptyComponents); 183 if (mData.layout.processStateDataIncluded) { 184 for (int processState = 0; processState < BatteryConsumer.PROCESS_STATE_COUNT; 185 processState++) { 186 if (processState == PROCESS_STATE_UNSPECIFIED) { 187 continue; 188 } 189 dump(sb, id, processState, screenState, powerState, skipEmptyComponents); 190 } 191 } 192 } 193 194 // Remove trailing spaces 195 while (!sb.isEmpty() && Character.isWhitespace(sb.charAt(sb.length() - 1))) { 196 sb.setLength(sb.length() - 1); 197 } 198 199 pw.println(sb); 200 } 201 dump(StringBuilder sb, @BatteryConsumer.PowerComponentId int powerComponent, @BatteryConsumer.ProcessState int processState, @BatteryConsumer.ScreenState int screenState, @BatteryConsumer.PowerState int powerState, boolean skipEmptyComponents)202 private void dump(StringBuilder sb, @BatteryConsumer.PowerComponentId int powerComponent, 203 @BatteryConsumer.ProcessState int processState, 204 @BatteryConsumer.ScreenState int screenState, 205 @BatteryConsumer.PowerState int powerState, boolean skipEmptyComponents) { 206 final double power = getConsumedPower(powerComponent, processState, screenState, 207 powerState); 208 final long durationMs = getUsageDurationMillis(powerComponent, processState, screenState, 209 powerState); 210 if (skipEmptyComponents && power == 0 && durationMs == 0) { 211 return; 212 } 213 214 sb.append(mData.layout.getPowerComponentName(powerComponent)); 215 if (processState != PROCESS_STATE_ANY) { 216 sb.append(':'); 217 sb.append(BatteryConsumer.processStateToString(processState)); 218 } 219 sb.append("="); 220 sb.append(BatteryStats.formatCharge(power)); 221 222 if (durationMs != 0) { 223 sb.append(" ("); 224 BatteryStats.formatTimeMsNoSpace(sb, durationMs); 225 sb.append(")"); 226 } 227 sb.append(' '); 228 } 229 230 /** Returns whether there are any atoms.proto POWER_COMPONENTS data to write to a proto. */ hasStatsProtoData()231 boolean hasStatsProtoData() { 232 return writeStatsProtoImpl(null); 233 } 234 235 /** Writes all atoms.proto POWER_COMPONENTS for this PowerComponents to the given proto. */ writeStatsProto(@onNull ProtoOutputStream proto)236 void writeStatsProto(@NonNull ProtoOutputStream proto) { 237 writeStatsProtoImpl(proto); 238 } 239 240 /** 241 * Returns whether there are any atoms.proto POWER_COMPONENTS data to write to a proto, 242 * and writes it to the given proto if it is non-null. 243 */ writeStatsProtoImpl(@ullable ProtoOutputStream proto)244 private boolean writeStatsProtoImpl(@Nullable ProtoOutputStream proto) { 245 boolean interestingData = false; 246 247 for (@BatteryConsumer.PowerComponentId int componentId : mData.layout.powerComponentIds) { 248 final BatteryConsumer.Key[] keys = mData.layout.getKeys(componentId); 249 for (BatteryConsumer.Key key : keys) { 250 final long powerDeciCoulombs = convertMahToDeciCoulombs( 251 getConsumedPower(key.powerComponentId, key.processState, key.screenState, 252 key.powerState)); 253 final long durationMs = getUsageDurationMillis(key.powerComponentId, 254 key.processState, key.screenState, key.powerState); 255 256 if (powerDeciCoulombs == 0 && durationMs == 0) { 257 // No interesting data. Make sure not to even write the COMPONENT int. 258 continue; 259 } 260 261 interestingData = true; 262 if (proto == null) { 263 // We're just asked whether there is data, not to actually write it. 264 // And there is. 265 return true; 266 } 267 268 if (key.processState == PROCESS_STATE_UNSPECIFIED) { 269 writePowerComponentUsage(proto, 270 BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS, 271 componentId, powerDeciCoulombs, durationMs); 272 } else { 273 writePowerUsageSlice(proto, componentId, powerDeciCoulombs, durationMs, 274 key.processState); 275 } 276 } 277 } 278 return interestingData; 279 } 280 writePowerUsageSlice(ProtoOutputStream proto, int componentId, long powerDeciCoulombs, long durationMs, int processState)281 private void writePowerUsageSlice(ProtoOutputStream proto, int componentId, 282 long powerDeciCoulombs, long durationMs, int processState) { 283 final long slicesToken = 284 proto.start(BatteryUsageStatsAtomsProto.BatteryConsumerData.SLICES); 285 writePowerComponentUsage(proto, 286 BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice 287 .POWER_COMPONENT, 288 componentId, powerDeciCoulombs, durationMs); 289 290 final int procState; 291 switch (processState) { 292 case BatteryConsumer.PROCESS_STATE_FOREGROUND: 293 procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice 294 .FOREGROUND; 295 break; 296 case BatteryConsumer.PROCESS_STATE_BACKGROUND: 297 procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice 298 .BACKGROUND; 299 break; 300 case BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE: 301 procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice 302 .FOREGROUND_SERVICE; 303 break; 304 case BatteryConsumer.PROCESS_STATE_CACHED: 305 procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice 306 .CACHED; 307 break; 308 default: 309 throw new IllegalArgumentException("Unknown process state: " + processState); 310 } 311 312 proto.write(BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice 313 .PROCESS_STATE, procState); 314 315 proto.end(slicesToken); 316 } 317 writePowerComponentUsage(ProtoOutputStream proto, long tag, @BatteryConsumer.PowerComponentId int componentId, long powerDeciCoulombs, long durationMs)318 private void writePowerComponentUsage(ProtoOutputStream proto, long tag, 319 @BatteryConsumer.PowerComponentId int componentId, long powerDeciCoulombs, 320 long durationMs) { 321 final long token = proto.start(tag); 322 proto.write( 323 BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage 324 .COMPONENT, 325 componentId); 326 proto.write( 327 BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage 328 .POWER_DECI_COULOMBS, 329 powerDeciCoulombs); 330 proto.write( 331 BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage 332 .DURATION_MILLIS, 333 durationMs); 334 proto.end(token); 335 } 336 writeToXml(TypedXmlSerializer serializer)337 void writeToXml(TypedXmlSerializer serializer) throws IOException { 338 serializer.startTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS); 339 for (BatteryConsumer.Key key : mData.layout.keys) { 340 if (!mData.hasValue(key.mPowerColumnIndex) 341 && !mData.hasValue(key.mDurationColumnIndex)) { 342 continue; 343 } 344 345 final double powerMah = getConsumedPower(key); 346 final long durationMs = getUsageDurationMillis(key); 347 if (powerMah == 0 && durationMs == 0) { 348 continue; 349 } 350 351 serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT); 352 serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, key.powerComponentId); 353 if (key.processState != PROCESS_STATE_UNSPECIFIED) { 354 serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_PROCESS_STATE, 355 key.processState); 356 } 357 if (key.screenState != SCREEN_STATE_UNSPECIFIED) { 358 serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_SCREEN_STATE, 359 key.screenState); 360 } 361 if (key.powerState != POWER_STATE_UNSPECIFIED) { 362 serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_POWER_STATE, 363 key.powerState); 364 } 365 if (powerMah != 0) { 366 serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah); 367 } 368 if (durationMs != 0) { 369 serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs); 370 } 371 serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT); 372 } 373 serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS); 374 } 375 376 // No longer part of the BatteryUsageStats XML format. Preserved for compatibility 377 private static final String XML_TAG_CUSTOM_COMPONENT_COMPAT = "custom_component"; 378 parseXml(TypedXmlPullParser parser, PowerComponents.Builder builder)379 static void parseXml(TypedXmlPullParser parser, PowerComponents.Builder builder) 380 throws XmlPullParserException, IOException { 381 int eventType = parser.getEventType(); 382 if (eventType != XmlPullParser.START_TAG || !parser.getName().equals( 383 BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { 384 throw new XmlPullParserException("Invalid XML parser state"); 385 } 386 387 while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals( 388 BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) 389 && eventType != XmlPullParser.END_DOCUMENT) { 390 if (eventType == XmlPullParser.START_TAG) { 391 switch (parser.getName()) { 392 case BatteryUsageStats.XML_TAG_COMPONENT: 393 case XML_TAG_CUSTOM_COMPONENT_COMPAT: { 394 int componentId = -1; 395 int processState = PROCESS_STATE_UNSPECIFIED; 396 int screenState = SCREEN_STATE_UNSPECIFIED; 397 int powerState = POWER_STATE_UNSPECIFIED; 398 double powerMah = 0; 399 long durationMs = 0; 400 for (int i = 0; i < parser.getAttributeCount(); i++) { 401 switch (parser.getAttributeName(i)) { 402 case BatteryUsageStats.XML_ATTR_ID: 403 componentId = parser.getAttributeInt(i); 404 break; 405 case BatteryUsageStats.XML_ATTR_PROCESS_STATE: 406 processState = parser.getAttributeInt(i); 407 break; 408 case BatteryUsageStats.XML_ATTR_SCREEN_STATE: 409 screenState = parser.getAttributeInt(i); 410 break; 411 case BatteryUsageStats.XML_ATTR_POWER_STATE: 412 powerState = parser.getAttributeInt(i); 413 break; 414 case BatteryUsageStats.XML_ATTR_POWER: 415 powerMah = parser.getAttributeDouble(i); 416 break; 417 case BatteryUsageStats.XML_ATTR_DURATION: 418 durationMs = parser.getAttributeLong(i); 419 break; 420 } 421 } 422 final BatteryConsumer.Key key = builder.mData.layout.getKey(componentId, 423 processState, screenState, powerState); 424 builder.addConsumedPower(key, powerMah); 425 builder.addUsageDurationMillis(key, durationMs); 426 break; 427 } 428 } 429 } 430 eventType = parser.next(); 431 } 432 } 433 434 /** 435 * Builder for PowerComponents. 436 */ 437 static final class Builder { 438 private final BatteryConsumer.BatteryConsumerData mData; 439 private final double mMinConsumedPowerThreshold; 440 Builder(BatteryConsumer.BatteryConsumerData data, double minConsumedPowerThreshold)441 Builder(BatteryConsumer.BatteryConsumerData data, double minConsumedPowerThreshold) { 442 mData = data; 443 mMinConsumedPowerThreshold = minConsumedPowerThreshold; 444 } 445 446 /** 447 * @deprecated use {@link #addConsumedPower(BatteryConsumer.Key, double)} 448 */ 449 @Deprecated 450 @NonNull setConsumedPower(BatteryConsumer.Key key, double componentPower)451 public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower) { 452 mData.putDouble(key.mPowerColumnIndex, componentPower); 453 return this; 454 } 455 456 @NonNull addConsumedPower(BatteryConsumer.Key key, double componentPower)457 public Builder addConsumedPower(BatteryConsumer.Key key, double componentPower) { 458 mData.putDouble(key.mPowerColumnIndex, 459 mData.getDouble(key.mPowerColumnIndex) + componentPower); 460 return this; 461 } 462 463 /** 464 * @deprecated use {@link #addUsageDurationMillis(BatteryConsumer.Key, long)} 465 */ 466 @Deprecated 467 @NonNull setUsageDurationMillis(BatteryConsumer.Key key, long componentUsageDurationMillis)468 public Builder setUsageDurationMillis(BatteryConsumer.Key key, 469 long componentUsageDurationMillis) { 470 mData.putLong(key.mDurationColumnIndex, componentUsageDurationMillis); 471 return this; 472 } 473 474 @NonNull addUsageDurationMillis(BatteryConsumer.Key key, long componentUsageDurationMillis)475 public Builder addUsageDurationMillis(BatteryConsumer.Key key, 476 long componentUsageDurationMillis) { 477 mData.putLong(key.mDurationColumnIndex, 478 mData.getLong(key.mDurationColumnIndex) + componentUsageDurationMillis); 479 return this; 480 } 481 addPowerAndDuration(PowerComponents.Builder other)482 public void addPowerAndDuration(PowerComponents.Builder other) { 483 addPowerAndDuration(other.mData); 484 } 485 addPowerAndDuration(PowerComponents other)486 public void addPowerAndDuration(PowerComponents other) { 487 addPowerAndDuration(other.mData); 488 } 489 addPowerAndDuration(BatteryConsumer.BatteryConsumerData otherData)490 private void addPowerAndDuration(BatteryConsumer.BatteryConsumerData otherData) { 491 if (mData.layout.customPowerComponentCount 492 != otherData.layout.customPowerComponentCount) { 493 throw new IllegalArgumentException( 494 "Number of custom power components does not match: " 495 + otherData.layout.customPowerComponentCount 496 + ", expected: " + mData.layout.customPowerComponentCount); 497 } 498 499 for (BatteryConsumer.Key key : mData.layout.keys) { 500 BatteryConsumer.Key otherKey = otherData.layout.getKey(key.powerComponentId, 501 key.processState, key.screenState, key.powerState); 502 if (otherKey == null) { 503 continue; 504 } 505 if (mData.hasValue(key.mPowerColumnIndex) 506 || otherData.hasValue(otherKey.mPowerColumnIndex)) { 507 mData.putDouble(key.mPowerColumnIndex, 508 mData.getDouble(key.mPowerColumnIndex) 509 + otherData.getDouble(otherKey.mPowerColumnIndex)); 510 } 511 if (mData.hasValue(key.mDurationColumnIndex) 512 || otherData.hasValue(otherKey.mDurationColumnIndex)) { 513 mData.putLong(key.mDurationColumnIndex, 514 mData.getLong(key.mDurationColumnIndex) 515 + otherData.getLong(otherKey.mDurationColumnIndex)); 516 } 517 } 518 } 519 520 /** 521 * Returns the total power accumulated by this builder so far. It may change 522 * by the time the {@code build()} method is called. 523 */ getTotalPower()524 public double getTotalPower() { 525 double totalPowerMah = 0; 526 for (BatteryConsumer.Key key : mData.layout.keys) { 527 if (key.processState == PROCESS_STATE_UNSPECIFIED 528 && key.screenState == SCREEN_STATE_UNSPECIFIED 529 && key.powerState == POWER_STATE_UNSPECIFIED) { 530 totalPowerMah += mData.getDouble(key.mPowerColumnIndex); 531 } 532 } 533 return totalPowerMah; 534 } 535 536 /** 537 * Creates a read-only object out of the Builder values. 538 */ 539 @NonNull build()540 public PowerComponents build() { 541 for (BatteryConsumer.Key key : mData.layout.keys) { 542 if (mMinConsumedPowerThreshold != 0) { 543 if (mData.getDouble(key.mPowerColumnIndex) < mMinConsumedPowerThreshold) { 544 mData.putDouble(key.mPowerColumnIndex, 0); 545 } 546 } 547 } 548 549 mData.putDouble(mData.layout.totalConsumedPowerColumnIndex, getTotalPower()); 550 return new PowerComponents(this); 551 } 552 } 553 } 554