• 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 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