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