• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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