• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.power.stats.processor;
18 
19 import android.annotation.CheckResult;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.os.BatteryStats;
23 import android.os.UserHandle;
24 import android.util.IndentingPrintWriter;
25 import android.util.IntArray;
26 import android.util.Slog;
27 import android.util.SparseArray;
28 
29 import com.android.internal.os.PowerStats;
30 import com.android.modules.utils.TypedXmlPullParser;
31 import com.android.modules.utils.TypedXmlSerializer;
32 
33 import org.xmlpull.v1.XmlPullParser;
34 import org.xmlpull.v1.XmlPullParserException;
35 
36 import java.io.IOException;
37 import java.io.StringWriter;
38 import java.util.Arrays;
39 import java.util.function.IntConsumer;
40 
41 /**
42  * Aggregated power stats for a specific power component (e.g. CPU, WiFi, etc). This class
43  * treats stats as arrays of nonspecific longs. Subclasses contain specific logic to interpret those
44  * longs and use them for calculations such as power attribution. They may use meta-data supplied
45  * as part of the {@link PowerStats.Descriptor}.
46  */
47 class PowerComponentAggregatedPowerStats {
48     private static final String TAG = "AggregatePowerStats";
49     static final String XML_TAG_POWER_COMPONENT = "power_component";
50     static final String XML_ATTR_ID = "id";
51     private static final String XML_TAG_DEVICE_STATS = "device-stats";
52     private static final String XML_TAG_STATE_STATS = "state-stats";
53     private static final String XML_ATTR_KEY = "key";
54     private static final String XML_TAG_UID_STATS = "uid-stats";
55     private static final String XML_ATTR_UID = "uid";
56     private static final long UNKNOWN = -1;
57 
58     public final int powerComponentId;
59     @NonNull
60     private final AggregatedPowerStats mAggregatedPowerStats;
61     @NonNull
62     private final AggregatedPowerStatsConfig.PowerComponent mConfig;
63     private final MultiStateStats.States[] mDeviceStateConfig;
64     private final MultiStateStats.States[] mUidStateConfig;
65     private final int[] mDeviceStates;
66 
67     private PowerStatsProcessor mProcessor;
68     private MultiStateStats.Factory mStatsFactory;
69     private MultiStateStats.Factory mStateStatsFactory;
70     private MultiStateStats.Factory mUidStatsFactory;
71     private PowerStats.Descriptor mPowerStatsDescriptor;
72     private long mPowerStatsTimestamp;
73     private MultiStateStats mDeviceStats;
74     private final SparseArray<MultiStateStats> mStateStats = new SparseArray<>();
75     private final SparseArray<UidStats> mUidStats = new SparseArray<>();
76 
77     private static class UidStats {
78         public int[] states;
79         public MultiStateStats stats;
80         public boolean hasPowerStats;
81         public boolean updated;
82     }
83 
PowerComponentAggregatedPowerStats(@onNull AggregatedPowerStats aggregatedPowerStats, @NonNull AggregatedPowerStatsConfig.PowerComponent config)84     PowerComponentAggregatedPowerStats(@NonNull AggregatedPowerStats aggregatedPowerStats,
85             @NonNull AggregatedPowerStatsConfig.PowerComponent config) {
86         mAggregatedPowerStats = aggregatedPowerStats;
87         mConfig = config;
88         powerComponentId = config.getPowerComponentId();
89         mDeviceStateConfig = config.getDeviceStateConfig();
90         mUidStateConfig = config.getUidStateConfig();
91         mDeviceStates = new int[mDeviceStateConfig.length];
92         mPowerStatsTimestamp = UNKNOWN;
93     }
94 
95     @NonNull
getAggregatedPowerStats()96     AggregatedPowerStats getAggregatedPowerStats() {
97         return mAggregatedPowerStats;
98     }
99 
100     @NonNull
getConfig()101     public AggregatedPowerStatsConfig.PowerComponent getConfig() {
102         return mConfig;
103     }
104 
105     @Nullable
getPowerStatsDescriptor()106     public PowerStats.Descriptor getPowerStatsDescriptor() {
107         return mPowerStatsDescriptor;
108     }
109 
setPowerStatsDescriptor(PowerStats.Descriptor powerStatsDescriptor)110     public void setPowerStatsDescriptor(PowerStats.Descriptor powerStatsDescriptor) {
111         mPowerStatsDescriptor = powerStatsDescriptor;
112     }
113 
start(long timestampMs)114     void start(long timestampMs) {
115         if (mProcessor == null) {
116             mProcessor = mConfig.createProcessor();
117         }
118         mProcessor.start(this, timestampMs);
119     }
120 
finish(long timestampMs)121     void finish(long timestampMs) {
122         mProcessor.finish(this, timestampMs);
123     }
124 
noteStateChange(BatteryStats.HistoryItem item)125     void noteStateChange(BatteryStats.HistoryItem item) {
126         mProcessor.noteStateChange(this, item);
127     }
128 
noteBatteryLevel(int batteryLevel, int batteryChargeUah, long timestampMs)129     public void noteBatteryLevel(int batteryLevel, int batteryChargeUah, long timestampMs) {
130         mProcessor.noteBatteryLevel(batteryLevel, batteryChargeUah, timestampMs);
131     }
132 
setState(@ggregatedPowerStatsConfig.TrackedState int stateId, int state, long timestampMs)133     void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state,
134             long timestampMs) {
135         if (mDeviceStats == null) {
136             createDeviceStats(timestampMs);
137         }
138 
139         mDeviceStates[stateId] = state;
140 
141         if (mDeviceStateConfig[stateId].isTracked()) {
142             if (mDeviceStats != null) {
143                 mDeviceStats.setState(stateId, state, timestampMs);
144             }
145             for (int i = mStateStats.size() - 1; i >= 0; i--) {
146                 MultiStateStats stateStats = mStateStats.valueAt(i);
147                 stateStats.setState(stateId, state, timestampMs);
148             }
149         }
150 
151         int uidStateId = MultiStateStats.States
152                 .findTrackedStateByName(mUidStateConfig, mDeviceStateConfig[stateId].getName());
153         if (uidStateId != MultiStateStats.STATE_DOES_NOT_EXIST
154                 && mUidStateConfig[uidStateId].isTracked()) {
155             for (int i = mUidStats.size() - 1; i >= 0; i--) {
156                 PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
157                 if (uidStats.stats == null) {
158                     createUidStats(uidStats, timestampMs);
159                 }
160 
161                 uidStats.states[uidStateId] = state;
162                 if (uidStats.stats != null) {
163                     uidStats.stats.setState(uidStateId, state, timestampMs);
164                 }
165             }
166         }
167     }
168 
setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state, long timestampMs)169     void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state,
170             long timestampMs) {
171         mProcessor.setUidState(this, uid, stateId, state, timestampMs);
172     }
173 
setProcessedUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state, long timestampMs)174     void setProcessedUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId,
175             int state, long timestampMs) {
176         if (!mUidStateConfig[stateId].isTracked()) {
177             return;
178         }
179 
180         UidStats uidStats = getUidStats(uid);
181         if (uidStats.stats == null) {
182             createUidStats(uidStats, timestampMs);
183         }
184 
185         uidStats.states[stateId] = state;
186 
187         if (uidStats.stats != null) {
188             uidStats.stats.setState(stateId, state, timestampMs);
189         }
190     }
191 
setDeviceStats(int[] states, long[] values)192     void setDeviceStats(int[] states, long[] values) {
193         if (mDeviceStats == null) {
194             createDeviceStats(0);
195         }
196         mDeviceStats.setStats(states, values);
197     }
198 
setUidStats(int uid, int[] states, long[] values)199     void setUidStats(int uid, int[] states, long[] values) {
200         UidStats uidStats = getUidStats(uid);
201         if (uidStats.stats == null) {
202             createUidStats(uidStats, mPowerStatsTimestamp);
203         }
204         uidStats.hasPowerStats = true;
205         uidStats.stats.setStats(states, values);
206     }
207 
isCompatible(PowerStats powerStats)208     boolean isCompatible(PowerStats powerStats) {
209         return mPowerStatsDescriptor == null || mPowerStatsDescriptor.equals(powerStats.descriptor);
210     }
211 
addPowerStats(PowerStats powerStats, long timestampMs)212     void addPowerStats(PowerStats powerStats, long timestampMs) {
213         // Should call powerStats.addProcessedPowerStats
214         mProcessor.addPowerStats(this, powerStats, timestampMs);
215     }
216 
217     /**
218      * Should be called ONLY by PowerStatsProcessor.processPowerStats.
219      */
addProcessedPowerStats(PowerStats powerStats, long timestampMs)220     void addProcessedPowerStats(PowerStats powerStats, long timestampMs) {
221         mPowerStatsDescriptor = powerStats.descriptor;
222 
223         if (mDeviceStats == null) {
224             createDeviceStats(timestampMs);
225         }
226 
227         for (int i = powerStats.stateStats.size() - 1; i >= 0; i--) {
228             int key = powerStats.stateStats.keyAt(i);
229             MultiStateStats stateStats = mStateStats.get(key);
230             if (stateStats == null) {
231                 stateStats = createStateStats(key, timestampMs);
232             }
233             stateStats.increment(powerStats.stateStats.valueAt(i), timestampMs);
234         }
235         mDeviceStats.increment(powerStats.stats, timestampMs);
236 
237         for (int i = powerStats.uidStats.size() - 1; i >= 0; i--) {
238             int uid = powerStats.uidStats.keyAt(i);
239             PowerComponentAggregatedPowerStats.UidStats uidStats = getUidStats(uid);
240             if (uidStats.stats == null) {
241                 createUidStats(uidStats, timestampMs);
242             }
243             uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs);
244             uidStats.updated = true;
245             uidStats.hasPowerStats = true;
246         }
247 
248         // For UIDs not mentioned in the PowerStats object, we must assume a 0 increment.
249         // It is essential to call `stats.increment(zero)` in order to record the new
250         // timestamp, which will ensure correct proportional attribution across all UIDs
251         for (int i = mUidStats.size() - 1; i >= 0; i--) {
252             PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
253             if (!uidStats.updated && uidStats.stats != null) {
254                 // Null stands for an array of zeros
255                 uidStats.stats.increment(null, timestampMs);
256             }
257             uidStats.updated = false;
258         }
259 
260         mPowerStatsTimestamp = timestampMs;
261     }
262 
reset()263     void reset() {
264         mStatsFactory = null;
265         mUidStatsFactory = null;
266         mDeviceStats = null;
267         mStateStats.clear();
268         for (int i = mUidStats.size() - 1; i >= 0; i--) {
269             mUidStats.valueAt(i).stats = null;
270             mUidStats.valueAt(i).hasPowerStats = false;
271         }
272     }
273 
getUidStats(int uid)274     private UidStats getUidStats(int uid) {
275         UidStats uidStats = mUidStats.get(uid);
276         if (uidStats == null) {
277             uidStats = new UidStats();
278             uidStats.states = new int[mUidStateConfig.length];
279             for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
280                 if (mUidStateConfig[stateId].isTracked()) {
281                     int deviceStateId = MultiStateStats.States.findTrackedStateByName(
282                             mDeviceStateConfig, mUidStateConfig[stateId].getName());
283                     if (deviceStateId != MultiStateStats.STATE_DOES_NOT_EXIST
284                             && mDeviceStateConfig[deviceStateId].isTracked()) {
285                         uidStats.states[stateId] = mDeviceStates[deviceStateId];
286                     }
287                 }
288             }
289             mUidStats.put(uid, uidStats);
290         }
291         return uidStats;
292     }
293 
getUids()294     IntArray getUids() {
295         IntArray uids = new IntArray(mUidStats.size());
296         for (int i = mUidStats.size() - 1; i >= 0; i--) {
297             UidStats uidStats = mUidStats.valueAt(i);
298             if (uidStats.stats != null) {
299                 uids.add(mUidStats.keyAt(i));
300             }
301         }
302         return uids;
303     }
304 
getActiveUids()305     IntArray getActiveUids() {
306         IntArray uids = new IntArray(mUidStats.size());
307         for (int i = mUidStats.size() - 1; i >= 0; i--) {
308             UidStats uidStats = mUidStats.valueAt(i);
309             if (uidStats.hasPowerStats) {
310                 uids.add(mUidStats.keyAt(i));
311             }
312         }
313         return uids;
314     }
315 
316     /**
317      * Populates outValues with the stats for the specified states. If the stats are all 0,
318      * returns false, leaving outValues unchanged.
319      */
320     @CheckResult
getDeviceStats(long[] outValues, int[] deviceStates)321     boolean getDeviceStats(long[] outValues, int[] deviceStates) {
322         if (deviceStates.length != mDeviceStateConfig.length) {
323             throw new IllegalArgumentException(
324                     "Invalid number of tracked states: " + deviceStates.length
325                     + " expected: " + mDeviceStateConfig.length);
326         }
327         if (mDeviceStats != null) {
328             return mDeviceStats.getStats(outValues, deviceStates);
329         }
330         return false;
331     }
332 
333     /**
334      * Populates outValues with the stats for the specified key and device states. If the stats
335      * are all 0, returns false, leaving outValues unchanged.
336      */
337     @CheckResult
getStateStats(long[] outValues, int key, int[] deviceStates)338     boolean getStateStats(long[] outValues, int key, int[] deviceStates) {
339         if (deviceStates.length != mDeviceStateConfig.length) {
340             throw new IllegalArgumentException(
341                     "Invalid number of tracked states: " + deviceStates.length
342                             + " expected: " + mDeviceStateConfig.length);
343         }
344         MultiStateStats stateStats = mStateStats.get(key);
345         if (stateStats != null) {
346             return stateStats.getStats(outValues, deviceStates);
347         }
348         return false;
349     }
350 
forEachStateStatsKey(IntConsumer consumer)351     void forEachStateStatsKey(IntConsumer consumer) {
352         for (int i = mStateStats.size() - 1; i >= 0; i--) {
353             consumer.accept(mStateStats.keyAt(i));
354         }
355     }
356 
357     /**
358      * Populates outValues with the stats for the specified UID and UID states. If the stats are
359      *  all 0, returns false, leaving outValues unchanged.
360      */
361     @CheckResult
getUidStats(long[] outValues, int uid, int[] uidStates)362     boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
363         if (uidStates.length != mUidStateConfig.length) {
364             throw new IllegalArgumentException(
365                     "Invalid number of tracked states: " + uidStates.length
366                     + " expected: " + mUidStateConfig.length);
367         }
368         UidStats uidStats = mUidStats.get(uid);
369         if (uidStats != null && uidStats.stats != null) {
370             return uidStats.stats.getStats(outValues, uidStates);
371         }
372         return false;
373     }
374 
createDeviceStats(long timestampMs)375     private void createDeviceStats(long timestampMs) {
376         if (mStatsFactory == null) {
377             if (mPowerStatsDescriptor == null) {
378                 return;
379             }
380             mStatsFactory = new MultiStateStats.Factory(
381                     mPowerStatsDescriptor.statsArrayLength, mDeviceStateConfig);
382         }
383 
384         mDeviceStats = mStatsFactory.create();
385         if (mPowerStatsTimestamp != UNKNOWN) {
386             timestampMs = mPowerStatsTimestamp;
387         }
388         if (timestampMs != UNKNOWN) {
389             for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
390                 int state = mDeviceStates[stateId];
391                 mDeviceStats.setState(stateId, state, timestampMs);
392                 for (int i = mStateStats.size() - 1; i >= 0; i--) {
393                     MultiStateStats stateStats = mStateStats.valueAt(i);
394                     stateStats.setState(stateId, state, timestampMs);
395                 }
396             }
397         }
398     }
399 
createStateStats(int key, long timestampMs)400     private MultiStateStats createStateStats(int key, long timestampMs) {
401         if (mStateStatsFactory == null) {
402             if (mPowerStatsDescriptor == null) {
403                 return null;
404             }
405             mStateStatsFactory = new MultiStateStats.Factory(
406                     mPowerStatsDescriptor.stateStatsArrayLength, mDeviceStateConfig);
407         }
408 
409         MultiStateStats stateStats = mStateStatsFactory.create();
410         mStateStats.put(key, stateStats);
411         if (mDeviceStats != null) {
412             stateStats.copyStatesFrom(mDeviceStats);
413         }
414 
415         return stateStats;
416     }
417 
createUidStats(UidStats uidStats, long timestampMs)418     private void createUidStats(UidStats uidStats, long timestampMs) {
419         if (mUidStatsFactory == null) {
420             if (mPowerStatsDescriptor == null) {
421                 return;
422             }
423             mUidStatsFactory = new MultiStateStats.Factory(
424                     mPowerStatsDescriptor.uidStatsArrayLength, mUidStateConfig);
425         }
426 
427         uidStats.stats = mUidStatsFactory.create();
428 
429         if (mPowerStatsTimestamp != UNKNOWN) {
430             timestampMs = mPowerStatsTimestamp;
431         }
432         if (timestampMs != UNKNOWN) {
433             for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
434                 uidStats.stats.setState(stateId, uidStats.states[stateId], timestampMs);
435             }
436         }
437     }
438 
copyStatesFrom(PowerComponentAggregatedPowerStats source)439     void copyStatesFrom(PowerComponentAggregatedPowerStats source) {
440         if (source.mDeviceStates.length == mDeviceStates.length) {
441             System.arraycopy(source.mDeviceStates, 0, mDeviceStates, 0, mDeviceStates.length);
442             if (source.mDeviceStats != null) {
443                 createDeviceStats(0);
444                 if (mDeviceStats != null) {
445                     mDeviceStats.copyStatesFrom(source.mDeviceStats);
446                 }
447             }
448         } else {
449             Slog.wtf(TAG, "State configurations have different lengths: "
450                     + source.mDeviceStates.length + " vs " + mDeviceStates.length);
451         }
452         for (int i = source.mUidStats.size() - 1; i >= 0; i--) {
453             int uid = source.mUidStats.keyAt(i);
454             UidStats sourceUidStats = source.mUidStats.valueAt(i);
455             if (sourceUidStats.states == null) {
456                 continue;
457             }
458             UidStats uidStats = new UidStats();
459             uidStats.states = Arrays.copyOf(sourceUidStats.states, sourceUidStats.states.length);
460             if (sourceUidStats.stats != null) {
461                 createUidStats(uidStats, 0);
462                 if (uidStats.stats != null) {
463                     uidStats.stats.copyStatesFrom(sourceUidStats.stats);
464                 }
465             }
466             mUidStats.put(uid, uidStats);
467         }
468     }
469 
writeXml(TypedXmlSerializer serializer)470     public void writeXml(TypedXmlSerializer serializer) throws IOException {
471         // No stats aggregated - can skip writing XML altogether
472         if (mPowerStatsDescriptor == null) {
473             return;
474         }
475 
476         serializer.startTag(null, XML_TAG_POWER_COMPONENT);
477         serializer.attributeInt(null, XML_ATTR_ID, powerComponentId);
478         mPowerStatsDescriptor.writeXml(serializer);
479 
480         if (mDeviceStats != null) {
481             serializer.startTag(null, XML_TAG_DEVICE_STATS);
482             mDeviceStats.writeXml(serializer);
483             serializer.endTag(null, XML_TAG_DEVICE_STATS);
484         }
485 
486         for (int i = 0; i < mStateStats.size(); i++) {
487             serializer.startTag(null, XML_TAG_STATE_STATS);
488             serializer.attributeInt(null, XML_ATTR_KEY, mStateStats.keyAt(i));
489             mStateStats.valueAt(i).writeXml(serializer);
490             serializer.endTag(null, XML_TAG_STATE_STATS);
491         }
492 
493         for (int i = mUidStats.size() - 1; i >= 0; i--) {
494             int uid = mUidStats.keyAt(i);
495             UidStats uidStats = mUidStats.valueAt(i);
496             if (uidStats.stats != null) {
497                 serializer.startTag(null, XML_TAG_UID_STATS);
498                 serializer.attributeInt(null, XML_ATTR_UID, uid);
499                 uidStats.stats.writeXml(serializer);
500                 serializer.endTag(null, XML_TAG_UID_STATS);
501             }
502         }
503 
504         serializer.endTag(null, XML_TAG_POWER_COMPONENT);
505         serializer.flush();
506     }
507 
readFromXml(TypedXmlPullParser parser)508     public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException,
509             IOException {
510         String outerTag = parser.getName();
511         int eventType = parser.getEventType();
512         while (eventType != XmlPullParser.END_DOCUMENT
513                 && !(eventType == XmlPullParser.END_TAG && parser.getName().equals(outerTag))) {
514             if (eventType == XmlPullParser.START_TAG) {
515                 switch (parser.getName()) {
516                     case PowerStats.Descriptor.XML_TAG_DESCRIPTOR:
517                         mPowerStatsDescriptor = PowerStats.Descriptor.createFromXml(parser);
518                         if (mPowerStatsDescriptor == null) {
519                             return false;
520                         }
521                         break;
522                     case XML_TAG_DEVICE_STATS:
523                         if (mDeviceStats == null) {
524                             createDeviceStats(UNKNOWN);
525                         }
526                         if (!mDeviceStats.readFromXml(parser)) {
527                             return false;
528                         }
529                         break;
530                     case XML_TAG_STATE_STATS:
531                         int key = parser.getAttributeInt(null, XML_ATTR_KEY);
532                         MultiStateStats stats = mStateStats.get(key);
533                         if (stats == null) {
534                             stats = createStateStats(key, UNKNOWN);
535                         }
536                         if (!stats.readFromXml(parser)) {
537                             return false;
538                         }
539                         break;
540                     case XML_TAG_UID_STATS:
541                         int uid = parser.getAttributeInt(null, XML_ATTR_UID);
542                         UidStats uidStats = getUidStats(uid);
543                         if (uidStats.stats == null) {
544                             createUidStats(uidStats, UNKNOWN);
545                         }
546                         uidStats.hasPowerStats = true;
547                         if (!uidStats.stats.readFromXml(parser)) {
548                             return false;
549                         }
550                         break;
551                 }
552             }
553             eventType = parser.next();
554         }
555         return true;
556     }
557 
dumpDevice(IndentingPrintWriter ipw)558     void dumpDevice(IndentingPrintWriter ipw) {
559         if (mDeviceStats != null) {
560             dumpMultiStateStats(ipw, mDeviceStats, mPowerStatsDescriptor.name, null,
561                     mPowerStatsDescriptor.getDeviceStatsFormatter());
562         }
563 
564         if (mStateStats.size() != 0) {
565             ipw.increaseIndent();
566             String header = mPowerStatsDescriptor.name + " states";
567             PowerStats.PowerStatsFormatter formatter =
568                     mPowerStatsDescriptor.getStateStatsFormatter();
569             for (int i = 0; i < mStateStats.size(); i++) {
570                 int key = mStateStats.keyAt(i);
571                 String stateLabel = mPowerStatsDescriptor.getStateLabel(key);
572                 MultiStateStats stateStats = mStateStats.valueAt(i);
573                 dumpMultiStateStats(ipw, stateStats, header, stateLabel, formatter);
574             }
575             ipw.decreaseIndent();
576         }
577     }
578 
dumpUid(IndentingPrintWriter ipw, int uid)579     void dumpUid(IndentingPrintWriter ipw, int uid) {
580         UidStats uidStats = mUidStats.get(uid);
581         if (uidStats != null && uidStats.stats != null) {
582             dumpMultiStateStats(ipw, uidStats.stats, mPowerStatsDescriptor.name, null,
583                     mPowerStatsDescriptor.getUidStatsFormatter());
584         }
585     }
586 
dumpMultiStateStats(IndentingPrintWriter ipw, MultiStateStats stats, String header, String additionalLabel, PowerStats.PowerStatsFormatter statsFormatter)587     private void dumpMultiStateStats(IndentingPrintWriter ipw, MultiStateStats stats,
588             String header, String additionalLabel,
589             PowerStats.PowerStatsFormatter statsFormatter) {
590         boolean[] firstLine = new boolean[]{true};
591         long[] values = new long[stats.getDimensionCount()];
592         MultiStateStats.States[] stateInfo = stats.getStates();
593         MultiStateStats.States.forEachTrackedStateCombination(stateInfo, states -> {
594             if (!stats.getStats(values, states)) {
595                 return;
596             }
597 
598             if (firstLine[0]) {
599                 ipw.println(header);
600                 ipw.increaseIndent();
601             }
602             firstLine[0] = false;
603             StringBuilder sb = new StringBuilder();
604             sb.append("(");
605             boolean first = true;
606             for (int i = 0; i < states.length; i++) {
607                 if (stateInfo[i].isTracked()) {
608                     if (!first) {
609                         sb.append(" ");
610                     }
611                     first = false;
612                     sb.append(stateInfo[i].getLabels()[states[i]]);
613                 }
614             }
615             if (additionalLabel != null) {
616                 sb.append(" ").append(additionalLabel);
617             }
618             sb.append(") ").append(statsFormatter.format(values));
619             ipw.println(sb);
620         });
621         if (!firstLine[0]) {
622             ipw.decreaseIndent();
623         }
624     }
625 
626     @Override
toString()627     public String toString() {
628         StringWriter sw = new StringWriter();
629         IndentingPrintWriter ipw = new IndentingPrintWriter(sw);
630         ipw.increaseIndent();
631         dumpDevice(ipw);
632         ipw.decreaseIndent();
633 
634         int[] uids = new int[mUidStats.size()];
635         for (int i = uids.length - 1; i >= 0; i--) {
636             uids[i] = mUidStats.keyAt(i);
637         }
638         Arrays.sort(uids);
639         for (int uid : uids) {
640             ipw.println(UserHandle.formatUid(uid));
641             ipw.increaseIndent();
642             dumpUid(ipw, uid);
643             ipw.decreaseIndent();
644         }
645 
646         ipw.flush();
647 
648         return sw.toString();
649     }
650 }
651