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