• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.format;
18 
19 import android.os.PersistableBundle;
20 import android.util.Slog;
21 
22 import com.android.internal.os.PowerStats;
23 
24 /**
25  * Captures the positions and lengths of sections of the stats array, such as usage duration,
26  * power usage estimates etc.
27  */
28 public class PowerStatsLayout {
29     private static final String TAG = "PowerStatsLayout";
30     private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
31     private static final String EXTRA_DEVICE_DURATION_POSITION = "dd";
32     private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
33     private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
34     private static final String EXTRA_UID_DURATION_POSITION = "ud";
35     private static final String EXTRA_UID_ENERGY_CONSUMERS_POSITION = "ue";
36     private static final String EXTRA_UID_ENERGY_CONSUMERS_COUNT = "uec";
37     private static final String EXTRA_UID_POWER_POSITION = "up";
38 
39     public static final int UNSUPPORTED = -1;
40     protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
41     protected static final int FLAG_OPTIONAL = 1;
42     protected static final int FLAG_HIDDEN = 2;
43     protected static final int FLAG_FORMAT_AS_POWER = 4;
44 
45     private int mDeviceStatsArrayLength;
46     private int mStateStatsArrayLength;
47     private int mUidStatsArrayLength;
48 
49     private final StringBuilder mDeviceFormat = new StringBuilder();
50     private final StringBuilder mStateFormat = new StringBuilder();
51     private final StringBuilder mUidFormat = new StringBuilder();
52 
53     protected int mDeviceDurationPosition = UNSUPPORTED;
54     private int mDeviceEnergyConsumerPosition;
55     private int mDeviceEnergyConsumerCount;
56     private int mDevicePowerEstimatePosition = UNSUPPORTED;
57     private int mUidDurationPosition = UNSUPPORTED;
58     private int mUidEnergyConsumerPosition = UNSUPPORTED;
59     private int mUidEnergyConsumerCount;
60     private int mUidPowerEstimatePosition = UNSUPPORTED;
61 
PowerStatsLayout()62     public PowerStatsLayout() {
63     }
64 
PowerStatsLayout(PowerStats.Descriptor descriptor)65     public PowerStatsLayout(PowerStats.Descriptor descriptor) {
66         PersistableBundle extras = descriptor.extras;
67         mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION);
68         mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
69         mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
70         mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
71         mUidDurationPosition = extras.getInt(EXTRA_UID_DURATION_POSITION);
72         mUidEnergyConsumerPosition = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION);
73         mUidEnergyConsumerCount = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT);
74         mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
75     }
76 
77     /**
78      * Copies the elements of the stats array layout into <code>extras</code>
79      */
toExtras(PersistableBundle extras)80     public void toExtras(PersistableBundle extras) {
81         extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition);
82         extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION, mDeviceEnergyConsumerPosition);
83         extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT, mDeviceEnergyConsumerCount);
84         extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
85         extras.putInt(EXTRA_UID_DURATION_POSITION, mUidDurationPosition);
86         extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION, mUidEnergyConsumerPosition);
87         extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT, mUidEnergyConsumerCount);
88         extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
89         extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, mDeviceFormat.toString());
90         extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, mStateFormat.toString());
91         extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, mUidFormat.toString());
92     }
93 
getDeviceStatsArrayLength()94     public int getDeviceStatsArrayLength() {
95         return mDeviceStatsArrayLength;
96     }
97 
getStateStatsArrayLength()98     public int getStateStatsArrayLength() {
99         return mStateStatsArrayLength;
100     }
101 
getUidStatsArrayLength()102     public int getUidStatsArrayLength() {
103         return mUidStatsArrayLength;
104     }
105 
106     /**
107      * @param label should not contain either spaces or colons
108      */
appendFormat(StringBuilder sb, int position, int length, String label, int flags)109     private void appendFormat(StringBuilder sb, int position, int length, String label,
110             int flags) {
111         if ((flags & FLAG_HIDDEN) != 0) {
112             return;
113         }
114 
115         if (!sb.isEmpty()) {
116             sb.append(' ');
117         }
118 
119         sb.append(label).append(':');
120         sb.append(position);
121         if (length != 1) {
122             sb.append('[').append(length).append(']');
123         }
124         if ((flags & FLAG_FORMAT_AS_POWER) != 0) {
125             sb.append('p');
126         }
127         if ((flags & FLAG_OPTIONAL) != 0) {
128             sb.append('?');
129         }
130     }
131 
addDeviceSection(int length, String label, int flags)132     protected int addDeviceSection(int length, String label, int flags) {
133         int position = mDeviceStatsArrayLength;
134         mDeviceStatsArrayLength += length;
135         appendFormat(mDeviceFormat, position, length, label, flags);
136         return position;
137     }
138 
addDeviceSection(int length, String label)139     protected int addDeviceSection(int length, String label) {
140         return addDeviceSection(length, label, 0);
141     }
142 
addStateSection(int length, String label, int flags)143     protected int addStateSection(int length, String label, int flags) {
144         int position = mStateStatsArrayLength;
145         mStateStatsArrayLength += length;
146         appendFormat(mStateFormat, position, length, label, flags);
147         return position;
148     }
149 
addStateSection(int length, String label)150     protected int addStateSection(int length, String label) {
151         return addStateSection(length, label, 0);
152     }
153 
154 
addUidSection(int length, String label, int flags)155     protected int addUidSection(int length, String label, int flags) {
156         int position = mUidStatsArrayLength;
157         mUidStatsArrayLength += length;
158         appendFormat(mUidFormat, position, length, label, flags);
159         return position;
160     }
161 
addUidSection(int length, String label)162     protected int addUidSection(int length, String label) {
163         return addUidSection(length, label, 0);
164     }
165 
166     /**
167      * Declare that the stats array has a section capturing usage duration
168      */
addDeviceSectionUsageDuration()169     protected void addDeviceSectionUsageDuration() {
170         mDeviceDurationPosition = addDeviceSection(1, "usage", FLAG_OPTIONAL);
171     }
172 
173     /**
174      * Saves the usage duration in the corresponding <code>stats</code> element.
175      */
setUsageDuration(long[] stats, long value)176     public void setUsageDuration(long[] stats, long value) {
177         stats[mDeviceDurationPosition] = value;
178     }
179 
180     /**
181      * Extracts the usage duration from the corresponding <code>stats</code> element.
182      */
getUsageDuration(long[] stats)183     public long getUsageDuration(long[] stats) {
184         if (mDeviceDurationPosition == UNSUPPORTED) {
185             return 0;
186         }
187         return stats[mDeviceDurationPosition];
188     }
189 
190     /**
191      * Declares that the stats array has a section capturing EnergyConsumer data from
192      * PowerStatsService.
193      */
addDeviceSectionEnergyConsumers(int energyConsumerCount)194     protected void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
195         mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount, "energy",
196                 FLAG_OPTIONAL);
197         mDeviceEnergyConsumerCount = energyConsumerCount;
198     }
199 
getEnergyConsumerCount()200     public int getEnergyConsumerCount() {
201         return mDeviceEnergyConsumerCount;
202     }
203 
204     /**
205      * Saves the accumulated energy for the specified rail the corresponding
206      * <code>stats</code> element.
207      */
setConsumedEnergy(long[] stats, int index, long energy)208     public void setConsumedEnergy(long[] stats, int index, long energy) {
209         stats[mDeviceEnergyConsumerPosition + index] = energy;
210     }
211 
212     /**
213      * Extracts the EnergyConsumer data from a device stats array for the specified
214      * EnergyConsumer.
215      */
getConsumedEnergy(long[] stats, int index)216     public long getConsumedEnergy(long[] stats, int index) {
217         return stats[mDeviceEnergyConsumerPosition + index];
218     }
219 
220     /**
221      * Declare that the stats array has a section capturing a power estimate
222      */
addDeviceSectionPowerEstimate()223     protected void addDeviceSectionPowerEstimate() {
224         mDevicePowerEstimatePosition = addDeviceSection(1, "power",
225                 FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
226     }
227 
228     /**
229      * Converts the supplied mAh power estimate to a long and saves it in the corresponding
230      * element of <code>stats</code>.
231      */
setDevicePowerEstimate(long[] stats, double power)232     public void setDevicePowerEstimate(long[] stats, double power) {
233         stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
234     }
235 
236     /**
237      * Extracts the power estimate from a device stats array and converts it to mAh.
238      */
getDevicePowerEstimate(long[] stats)239     public double getDevicePowerEstimate(long[] stats) {
240         if (mDevicePowerEstimatePosition == UNSUPPORTED) {
241             return 0;
242         }
243         return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
244     }
245 
246     /**
247      * Declare that the UID stats array has a section capturing usage duration
248      */
addUidSectionUsageDuration()249     protected void addUidSectionUsageDuration() {
250         mUidDurationPosition = addUidSection(1, "time");
251     }
252 
253     /**
254      * Declare that the UID stats array has a section capturing a power estimate
255      */
addUidSectionPowerEstimate()256     protected void addUidSectionPowerEstimate() {
257         mUidPowerEstimatePosition = addUidSection(1, "power", FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
258     }
259 
260     /**
261      * Returns true if power for this component is attributed to UIDs (apps).
262      */
isUidPowerAttributionSupported()263     public boolean isUidPowerAttributionSupported() {
264         return mUidPowerEstimatePosition != UNSUPPORTED || mUidDurationPosition != UNSUPPORTED;
265     }
266 
267     /**
268      * Saves usage duration it in the corresponding element of <code>stats</code>.
269      */
setUidUsageDuration(long[] stats, long durationMs)270     public void setUidUsageDuration(long[] stats, long durationMs) {
271         stats[mUidDurationPosition] = durationMs;
272     }
273 
274     /**
275      * Extracts the usage duration from a UID stats array.
276      */
getUidUsageDuration(long[] stats)277     public long getUidUsageDuration(long[] stats) {
278         if (mUidDurationPosition == UNSUPPORTED) {
279             return 0;
280         }
281         return stats[mUidDurationPosition];
282     }
283 
284     /**
285      * Declares that the UID stats array has a section capturing EnergyConsumer data from
286      * PowerStatsService.
287      */
addUidSectionEnergyConsumers(int energyConsumerCount)288     protected void addUidSectionEnergyConsumers(int energyConsumerCount) {
289         mUidEnergyConsumerPosition = addUidSection(energyConsumerCount, "energy",
290                 FLAG_OPTIONAL);
291         mUidEnergyConsumerCount = energyConsumerCount;
292     }
293 
getUidEnergyConsumerCount()294     public int getUidEnergyConsumerCount() {
295         return mUidEnergyConsumerCount;
296     }
297 
298     /**
299      * Saves the accumulated energy for the specified rail the corresponding
300      * <code>stats</code> element.
301      */
setUidConsumedEnergy(long[] stats, int index, long energy)302     public void setUidConsumedEnergy(long[] stats, int index, long energy) {
303         stats[mUidEnergyConsumerPosition + index] = energy;
304     }
305 
306     /**
307      * Extracts the EnergyConsumer data from a uid stats array for the specified
308      * EnergyConsumer.
309      */
getUidConsumedEnergy(long[] stats, int index)310     public long getUidConsumedEnergy(long[] stats, int index) {
311         return stats[mUidEnergyConsumerPosition + index];
312     }
313 
314     /**
315      * Converts the supplied mAh power estimate to a long and saves it in the corresponding
316      * element of <code>stats</code>.
317      */
setUidPowerEstimate(long[] stats, double power)318     public void setUidPowerEstimate(long[] stats, double power) {
319         stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
320     }
321 
322     /**
323      * Extracts the power estimate from a UID stats array and converts it to mAh.
324      */
getUidPowerEstimate(long[] stats)325     public double getUidPowerEstimate(long[] stats) {
326         if (mUidPowerEstimatePosition == UNSUPPORTED) {
327             return 0;
328         }
329         return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
330     }
331 
putIntArray(PersistableBundle extras, String key, int[] array)332     protected void putIntArray(PersistableBundle extras, String key, int[] array) {
333         if (array == null) {
334             return;
335         }
336 
337         StringBuilder sb = new StringBuilder();
338         for (int value : array) {
339             if (!sb.isEmpty()) {
340                 sb.append(',');
341             }
342             sb.append(value);
343         }
344         extras.putString(key, sb.toString());
345     }
346 
getIntArray(PersistableBundle extras, String key)347     protected int[] getIntArray(PersistableBundle extras, String key) {
348         String string = extras.getString(key);
349         if (string == null) {
350             return null;
351         }
352         String[] values = string.trim().split(",");
353         int[] result = new int[values.length];
354         for (int i = 0; i < values.length; i++) {
355             try {
356                 result[i] = Integer.parseInt(values[i]);
357             } catch (NumberFormatException e) {
358                 Slog.wtf(TAG, "Invalid CSV format: " + string);
359                 return null;
360             }
361         }
362         return result;
363     }
364 }
365