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 package com.android.server.power.stats.processor; 17 18 import static com.android.server.power.stats.processor.MultiStateStats.STATE_DOES_NOT_EXIST; 19 import static com.android.server.power.stats.processor.MultiStateStats.States.findTrackedStateByName; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.os.BatteryStats; 24 import android.util.Log; 25 26 import com.android.internal.os.PowerStats; 27 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.List; 32 33 /* 34 * The power estimation algorithm used by PowerStatsProcessor can roughly be 35 * described like this: 36 * 37 * 1. Estimate power usage for each state combination (e.g. power-battery/screen-on) using 38 * a metric such as CPU time-in-state. 39 * 40 * 2. Combine estimates obtain in step 1, aggregating across states that are *not* tracked 41 * per UID. 42 * 43 * 2. For each UID, compute the proportion of the combined estimates in each state 44 * and attribute the corresponding portion of the total power estimate in that state to the UID. 45 */ 46 abstract class PowerStatsProcessor { 47 private static final String TAG = "PowerStatsProcessor"; 48 49 private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0; 50 start(PowerComponentAggregatedPowerStats stats, long timestampMs)51 void start(PowerComponentAggregatedPowerStats stats, long timestampMs) { 52 } 53 noteStateChange(PowerComponentAggregatedPowerStats stats, BatteryStats.HistoryItem item)54 void noteStateChange(PowerComponentAggregatedPowerStats stats, 55 BatteryStats.HistoryItem item) { 56 } 57 noteBatteryLevel(int batteryLevel, int batteryChargeUah, long timestampMs)58 public void noteBatteryLevel(int batteryLevel, int batteryChargeUah, long timestampMs) { 59 } 60 setUidState(PowerComponentAggregatedPowerStats stats, int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state, long timestampMs)61 public void setUidState(PowerComponentAggregatedPowerStats stats, int uid, 62 @AggregatedPowerStatsConfig.TrackedState int stateId, int state, long timestampMs) { 63 stats.setProcessedUidState(uid, stateId, state, timestampMs); 64 } 65 addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats, long timestampMs)66 void addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats, 67 long timestampMs) { 68 stats.addProcessedPowerStats(powerStats, timestampMs); 69 } 70 finish(PowerComponentAggregatedPowerStats stats, long timestampMs)71 abstract void finish(PowerComponentAggregatedPowerStats stats, long timestampMs); 72 73 protected static class PowerEstimationPlan { 74 private final AggregatedPowerStatsConfig.PowerComponent mConfig; 75 public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>(); 76 public List<CombinedDeviceStateEstimate> combinedDeviceStateEstimations = new ArrayList<>(); 77 public List<UidStateEstimate> uidStateEstimates = new ArrayList<>(); 78 PowerEstimationPlan(AggregatedPowerStatsConfig.PowerComponent config)79 public PowerEstimationPlan(AggregatedPowerStatsConfig.PowerComponent config) { 80 mConfig = config; 81 addDeviceStateEstimations(); 82 combineDeviceStateEstimations(); 83 addUidStateEstimations(); 84 } 85 addDeviceStateEstimations()86 private void addDeviceStateEstimations() { 87 MultiStateStats.States[] config = mConfig.getDeviceStateConfig(); 88 int[][] deviceStateCombinations = getAllTrackedStateCombinations(config); 89 for (int[] deviceStateCombination : deviceStateCombinations) { 90 deviceStateEstimations.add( 91 new DeviceStateEstimation(config, deviceStateCombination)); 92 } 93 } 94 combineDeviceStateEstimations()95 private void combineDeviceStateEstimations() { 96 MultiStateStats.States[] deviceStateConfig = mConfig.getDeviceStateConfig(); 97 MultiStateStats.States[] uidStateConfig = mConfig.getUidStateConfig(); 98 MultiStateStats.States[] deviceStatesTrackedPerUid = 99 new MultiStateStats.States[deviceStateConfig.length]; 100 101 for (int i = 0; i < deviceStateConfig.length; i++) { 102 if (!deviceStateConfig[i].isTracked()) { 103 continue; 104 } 105 106 int index = findTrackedStateByName(uidStateConfig, deviceStateConfig[i].getName()); 107 if (index != STATE_DOES_NOT_EXIST && uidStateConfig[index].isTracked()) { 108 deviceStatesTrackedPerUid[i] = deviceStateConfig[i]; 109 } 110 } 111 112 combineDeviceStateEstimationsRecursively(deviceStateConfig, deviceStatesTrackedPerUid, 113 new int[deviceStateConfig.length], 0); 114 } 115 combineDeviceStateEstimationsRecursively( MultiStateStats.States[] deviceStateConfig, MultiStateStats.States[] deviceStatesTrackedPerUid, int[] stateValues, int state)116 private void combineDeviceStateEstimationsRecursively( 117 MultiStateStats.States[] deviceStateConfig, 118 MultiStateStats.States[] deviceStatesTrackedPerUid, int[] stateValues, int state) { 119 if (state >= deviceStateConfig.length) { 120 DeviceStateEstimation dse = getDeviceStateEstimate(stateValues); 121 CombinedDeviceStateEstimate cdse = getCombinedDeviceStateEstimate( 122 deviceStatesTrackedPerUid, stateValues); 123 if (cdse == null) { 124 cdse = new CombinedDeviceStateEstimate(deviceStatesTrackedPerUid, stateValues); 125 combinedDeviceStateEstimations.add(cdse); 126 } 127 cdse.deviceStateEstimations.add(dse); 128 return; 129 } 130 131 if (deviceStateConfig[state].isTracked()) { 132 for (int stateValue = 0; 133 stateValue < deviceStateConfig[state].getLabels().length; 134 stateValue++) { 135 stateValues[state] = stateValue; 136 combineDeviceStateEstimationsRecursively(deviceStateConfig, 137 deviceStatesTrackedPerUid, stateValues, state + 1); 138 } 139 } else { 140 combineDeviceStateEstimationsRecursively(deviceStateConfig, 141 deviceStatesTrackedPerUid, stateValues, state + 1); 142 } 143 } 144 addUidStateEstimations()145 private void addUidStateEstimations() { 146 MultiStateStats.States[] deviceStateConfig = mConfig.getDeviceStateConfig(); 147 MultiStateStats.States[] uidStateConfig = mConfig.getUidStateConfig(); 148 MultiStateStats.States[] uidStatesTrackedForDevice = 149 new MultiStateStats.States[uidStateConfig.length]; 150 MultiStateStats.States[] uidStatesNotTrackedForDevice = 151 new MultiStateStats.States[uidStateConfig.length]; 152 153 for (int i = 0; i < uidStateConfig.length; i++) { 154 if (!uidStateConfig[i].isTracked()) { 155 continue; 156 } 157 158 int index = findTrackedStateByName(deviceStateConfig, uidStateConfig[i].getName()); 159 if (index != STATE_DOES_NOT_EXIST && deviceStateConfig[index].isTracked()) { 160 uidStatesTrackedForDevice[i] = uidStateConfig[i]; 161 } else { 162 uidStatesNotTrackedForDevice[i] = uidStateConfig[i]; 163 } 164 } 165 166 @AggregatedPowerStatsConfig.TrackedState 167 int[][] uidStateCombinations = getAllTrackedStateCombinations(uidStateConfig); 168 for (int[] stateValues : uidStateCombinations) { 169 CombinedDeviceStateEstimate combined = 170 getCombinedDeviceStateEstimate(uidStatesTrackedForDevice, stateValues); 171 if (combined == null) { 172 // This is not supposed to be possible 173 Log.wtf(TAG, "Mismatch in UID and combined device states: " 174 + concatLabels(uidStatesTrackedForDevice, stateValues)); 175 continue; 176 } 177 UidStateEstimate uidStateEstimate = getUidStateEstimate(combined); 178 if (uidStateEstimate == null) { 179 uidStateEstimate = new UidStateEstimate(combined, uidStatesNotTrackedForDevice); 180 uidStateEstimates.add(uidStateEstimate); 181 } 182 uidStateEstimate.proportionalEstimates.add( 183 new UidStateProportionalEstimate(stateValues)); 184 } 185 } 186 187 @Override toString()188 public String toString() { 189 StringBuilder sb = new StringBuilder(); 190 sb.append("Step 1. Compute device-wide power estimates for state combinations:\n"); 191 for (DeviceStateEstimation deviceStateEstimation : deviceStateEstimations) { 192 sb.append(" ").append(deviceStateEstimation.id).append("\n"); 193 } 194 sb.append("Step 2. Combine device-wide estimates that are untracked per UID:\n"); 195 boolean any = false; 196 for (CombinedDeviceStateEstimate cdse : combinedDeviceStateEstimations) { 197 if (cdse.deviceStateEstimations.size() <= 1) { 198 continue; 199 } 200 any = true; 201 sb.append(" ").append(cdse.id).append(": "); 202 for (int i = 0; i < cdse.deviceStateEstimations.size(); i++) { 203 if (i != 0) { 204 sb.append(" + "); 205 } 206 sb.append(cdse.deviceStateEstimations.get(i).id); 207 } 208 sb.append("\n"); 209 } 210 if (!any) { 211 sb.append(" N/A\n"); 212 } 213 sb.append("Step 3. Proportionally distribute power estimates to UIDs:\n"); 214 for (UidStateEstimate uidStateEstimate : uidStateEstimates) { 215 sb.append(" ").append(uidStateEstimate.combinedDeviceStateEstimate.id) 216 .append("\n among: "); 217 for (int i = 0; i < uidStateEstimate.proportionalEstimates.size(); i++) { 218 UidStateProportionalEstimate uspe = 219 uidStateEstimate.proportionalEstimates.get(i); 220 if (i != 0) { 221 sb.append(", "); 222 } 223 sb.append(concatLabels(uidStateEstimate.states, uspe.stateValues)); 224 } 225 sb.append("\n"); 226 } 227 return sb.toString(); 228 } 229 230 @Nullable getDeviceStateEstimate(int[] stateValues)231 public DeviceStateEstimation getDeviceStateEstimate(int[] stateValues) { 232 String label = concatLabels(mConfig.getDeviceStateConfig(), stateValues); 233 for (int i = 0; i < deviceStateEstimations.size(); i++) { 234 DeviceStateEstimation deviceStateEstimation = this.deviceStateEstimations.get(i); 235 if (deviceStateEstimation.id.equals(label)) { 236 return deviceStateEstimation; 237 } 238 } 239 return null; 240 } 241 getCombinedDeviceStateEstimate( MultiStateStats.States[] deviceStates, int[] stateValues)242 public CombinedDeviceStateEstimate getCombinedDeviceStateEstimate( 243 MultiStateStats.States[] deviceStates, int[] stateValues) { 244 String label = concatLabels(deviceStates, stateValues); 245 for (int i = 0; i < combinedDeviceStateEstimations.size(); i++) { 246 CombinedDeviceStateEstimate cdse = combinedDeviceStateEstimations.get(i); 247 if (cdse.id.equals(label)) { 248 return cdse; 249 } 250 } 251 return null; 252 } 253 getUidStateEstimate(CombinedDeviceStateEstimate combined)254 public UidStateEstimate getUidStateEstimate(CombinedDeviceStateEstimate combined) { 255 for (int i = 0; i < uidStateEstimates.size(); i++) { 256 UidStateEstimate uidStateEstimate = uidStateEstimates.get(i); 257 if (uidStateEstimate.combinedDeviceStateEstimate == combined) { 258 return uidStateEstimate; 259 } 260 } 261 return null; 262 } 263 resetIntermediates()264 public void resetIntermediates() { 265 for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) { 266 deviceStateEstimations.get(i).intermediates = null; 267 } 268 for (int i = combinedDeviceStateEstimations.size() - 1; i >= 0; i--) { 269 combinedDeviceStateEstimations.get(i).intermediates = null; 270 } 271 for (int i = uidStateEstimates.size() - 1; i >= 0; i--) { 272 UidStateEstimate uidStateEstimate = uidStateEstimates.get(i); 273 List<UidStateProportionalEstimate> proportionalEstimates = 274 uidStateEstimate.proportionalEstimates; 275 for (int j = proportionalEstimates.size() - 1; j >= 0; j--) { 276 proportionalEstimates.get(j).intermediates = null; 277 } 278 } 279 } 280 } 281 282 protected static class DeviceStateEstimation { 283 public final String id; 284 public final int[] stateValues; 285 public Object intermediates; 286 DeviceStateEstimation(MultiStateStats.States[] config, int[] stateValues)287 public DeviceStateEstimation(MultiStateStats.States[] config, int[] stateValues) { 288 id = concatLabels(config, stateValues); 289 this.stateValues = stateValues; 290 } 291 } 292 293 protected static class CombinedDeviceStateEstimate { 294 public final String id; 295 public final int[] stateValues; 296 public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>(); 297 public Object intermediates; 298 CombinedDeviceStateEstimate(MultiStateStats.States[] config, int[] stateValues)299 public CombinedDeviceStateEstimate(MultiStateStats.States[] config, int[] stateValues) { 300 this.stateValues = Arrays.copyOf(stateValues, stateValues.length); 301 id = concatLabels(config, stateValues); 302 } 303 } 304 305 protected static class UidStateEstimate { 306 public final MultiStateStats.States[] states; 307 public CombinedDeviceStateEstimate combinedDeviceStateEstimate; 308 public List<UidStateProportionalEstimate> proportionalEstimates = new ArrayList<>(); 309 UidStateEstimate(CombinedDeviceStateEstimate combined, MultiStateStats.States[] states)310 public UidStateEstimate(CombinedDeviceStateEstimate combined, 311 MultiStateStats.States[] states) { 312 combinedDeviceStateEstimate = combined; 313 this.states = states; 314 } 315 } 316 317 protected static class UidStateProportionalEstimate { 318 public final int[] stateValues; 319 public Object intermediates; 320 UidStateProportionalEstimate(int[] stateValues)321 protected UidStateProportionalEstimate(int[] stateValues) { 322 this.stateValues = stateValues; 323 } 324 } 325 326 @NonNull concatLabels(MultiStateStats.States[] config, int[] stateValues)327 private static String concatLabels(MultiStateStats.States[] config, int[] stateValues) { 328 List<String> labels = new ArrayList<>(); 329 for (int state = 0; state < config.length; state++) { 330 if (config[state] != null && config[state].isTracked()) { 331 labels.add(config[state].getName() 332 + "=" + config[state].getLabels()[stateValues[state]]); 333 } 334 } 335 Collections.sort(labels); 336 return labels.toString(); 337 } 338 getAllTrackedStateCombinations(MultiStateStats.States[] states)339 private static int[][] getAllTrackedStateCombinations(MultiStateStats.States[] states) { 340 List<int[]> combinations = new ArrayList<>(); 341 MultiStateStats.States.forEachTrackedStateCombination(states, stateValues -> { 342 combinations.add(Arrays.copyOf(stateValues, stateValues.length)); 343 }); 344 return combinations.toArray(new int[combinations.size()][0]); 345 } 346 uCtoMah(long chargeUC)347 public static double uCtoMah(long chargeUC) { 348 return chargeUC * MILLIAMPHOUR_PER_MICROCOULOMB; 349 } 350 } 351