1 /*
2  * Copyright (C) 2015 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;
17 
18 import android.os.BatteryConsumer;
19 import android.os.BatteryStats;
20 import android.os.BatteryUsageStats;
21 import android.os.BatteryUsageStatsQuery;
22 import android.os.UidBatteryConsumer;
23 import android.util.ArrayMap;
24 import android.util.Log;
25 import android.util.SparseArray;
26 
27 import com.android.internal.os.CpuScalingPolicies;
28 import com.android.internal.os.PowerProfile;
29 
30 import java.util.Arrays;
31 
32 public class CpuPowerCalculator extends PowerCalculator {
33     private static final String TAG = "CpuPowerCalculator";
34     private static final boolean DEBUG = PowerCalculator.DEBUG;
35     private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
36 
37     private final CpuScalingPolicies mCpuScalingPolicies;
38     private final int mNumCpuClusters;
39 
40     // Time-in-state based CPU power estimation model computes the estimated power
41     // by adding up three components:
42     //   - CPU Active power:    the constant amount of charge consumed by the CPU when it is on
43     //   - Per Cluster power:   the additional amount of charge consumed by a CPU cluster
44     //                          when it is running
45     //   - Per frequency power: the additional amount of charge caused by dynamic frequency scaling
46 
47     private final UsageBasedPowerEstimator mCpuActivePowerEstimator;
48     // One estimator per cluster
49     private final UsageBasedPowerEstimator[] mPerClusterPowerEstimators;
50     // Multiple estimators per cluster: one per available scaling frequency. Note that different
51     // clusters have different sets of frequencies and corresponding power consumption averages.
52     private final UsageBasedPowerEstimator[][] mPerCpuFreqPowerEstimatorsByCluster;
53     // Flattened array of estimators across clusters
54     private final UsageBasedPowerEstimator[] mPerCpuFreqPowerEstimators;
55 
56     private static class Result {
57         public long durationMs;
58         public double powerMah;
59         public long durationFgMs;
60         public String packageWithHighestDrain;
61         public double[] perProcStatePowerMah;
62         public long[] cpuFreqTimes;
63     }
64 
CpuPowerCalculator(CpuScalingPolicies cpuScalingPolicies, PowerProfile profile)65     public CpuPowerCalculator(CpuScalingPolicies cpuScalingPolicies, PowerProfile profile) {
66         mCpuScalingPolicies = cpuScalingPolicies;
67         int[] policies = mCpuScalingPolicies.getPolicies();
68         mNumCpuClusters = policies.length;
69 
70         mCpuActivePowerEstimator = new UsageBasedPowerEstimator(
71                 profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE));
72 
73         mPerClusterPowerEstimators = new UsageBasedPowerEstimator[policies.length];
74         for (int i = 0; i < policies.length; i++) {
75             mPerClusterPowerEstimators[i] = new UsageBasedPowerEstimator(
76                     profile.getAveragePowerForCpuScalingPolicy(policies[i]));
77         }
78 
79         mPerCpuFreqPowerEstimators =
80                 new UsageBasedPowerEstimator[cpuScalingPolicies.getScalingStepCount()];
81         mPerCpuFreqPowerEstimatorsByCluster = new UsageBasedPowerEstimator[mNumCpuClusters][];
82         int index = 0;
83         for (int cluster = 0; cluster < policies.length; cluster++) {
84             int policy = policies[cluster];
85             int[] freqs = cpuScalingPolicies.getFrequencies(policy);
86             mPerCpuFreqPowerEstimatorsByCluster[cluster] =
87                     new UsageBasedPowerEstimator[freqs.length];
88             for (int step = 0; step < freqs.length; step++) {
89                 final UsageBasedPowerEstimator estimator = new UsageBasedPowerEstimator(
90                         profile.getAveragePowerForCpuScalingStep(policy, step));
91                 mPerCpuFreqPowerEstimatorsByCluster[cluster][step] = estimator;
92                 mPerCpuFreqPowerEstimators[index++] = estimator;
93             }
94         }
95     }
96 
97     @Override
isPowerComponentSupported(@atteryConsumer.PowerComponent int powerComponent)98     public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) {
99         return powerComponent == BatteryConsumer.POWER_COMPONENT_CPU;
100     }
101 
102     @Override
calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)103     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
104             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
105         double totalPowerMah = 0;
106 
107         BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
108         Result result = new Result();
109         if (query.isProcessStateDataNeeded()) {
110             result.cpuFreqTimes = new long[mCpuScalingPolicies.getScalingStepCount()];
111         }
112         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
113                 builder.getUidBatteryConsumerBuilders();
114         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
115             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
116             if (keys == UNINITIALIZED_KEYS) {
117                 if (query.isProcessStateDataNeeded()) {
118                     keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_CPU);
119                 } else {
120                     keys = null;
121                 }
122             }
123             calculateApp(app, app.getBatteryStatsUid(), query, result, keys);
124             if (!app.isVirtualUid()) {
125                 totalPowerMah += result.powerMah;
126             }
127         }
128 
129         final long consumptionUC = batteryStats.getCpuEnergyConsumptionUC();
130         final int powerModel = getPowerModel(consumptionUC, query);
131 
132         builder.getAggregateBatteryConsumerBuilder(
133                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
134                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, totalPowerMah);
135         builder.getAggregateBatteryConsumerBuilder(
136                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
137                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU,
138                         powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION
139                                 ? uCtoMah(consumptionUC) : totalPowerMah, powerModel);
140     }
141 
calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, BatteryUsageStatsQuery query, Result result, BatteryConsumer.Key[] keys)142     private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
143             BatteryUsageStatsQuery query, Result result, BatteryConsumer.Key[] keys) {
144         final long consumptionUC = u.getCpuEnergyConsumptionUC();
145         final int powerModel = getPowerModel(consumptionUC, query);
146         calculatePowerAndDuration(u, powerModel, consumptionUC, BatteryStats.STATS_SINCE_CHARGED,
147                 result);
148 
149         app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah, powerModel)
150                 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, result.durationMs)
151                 .setPackageWithHighestDrain(result.packageWithHighestDrain);
152 
153         if (query.isProcessStateDataNeeded() && keys != null) {
154             switch (powerModel) {
155                 case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
156                     calculateEnergyConsumptionPerProcessState(app, u, keys);
157                     break;
158                 case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
159                     calculateModeledPowerPerProcessState(app, u, keys, result);
160                     break;
161             }
162         }
163     }
164 
calculateEnergyConsumptionPerProcessState(UidBatteryConsumer.Builder app, BatteryStats.Uid u, BatteryConsumer.Key[] keys)165     private void calculateEnergyConsumptionPerProcessState(UidBatteryConsumer.Builder app,
166             BatteryStats.Uid u, BatteryConsumer.Key[] keys) {
167         for (BatteryConsumer.Key key : keys) {
168             // The key for PROCESS_STATE_UNSPECIFIED aka PROCESS_STATE_ANY has already been
169             // populated with the full energy across all states.  We don't want to override it with
170             // the energy for "other" states, which excludes the tracked states like
171             // foreground, background etc.
172             if (key.processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
173                 continue;
174             }
175 
176             final long consumptionUC = u.getCpuEnergyConsumptionUC(key.processState);
177             if (consumptionUC != 0) {
178                 app.setConsumedPower(key, uCtoMah(consumptionUC),
179                         BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
180             }
181         }
182     }
183 
calculateModeledPowerPerProcessState(UidBatteryConsumer.Builder app, BatteryStats.Uid u, BatteryConsumer.Key[] keys, Result result)184     private void calculateModeledPowerPerProcessState(UidBatteryConsumer.Builder app,
185             BatteryStats.Uid u, BatteryConsumer.Key[] keys, Result result) {
186         if (result.perProcStatePowerMah == null) {
187             result.perProcStatePowerMah = new double[BatteryConsumer.PROCESS_STATE_COUNT];
188         } else {
189             Arrays.fill(result.perProcStatePowerMah, 0);
190         }
191 
192         for (int uidProcState = 0; uidProcState < BatteryStats.Uid.NUM_PROCESS_STATE;
193                 uidProcState++) {
194             @BatteryConsumer.ProcessState int procState =
195                     BatteryStats.mapUidProcessStateToBatteryConsumerProcessState(uidProcState);
196             if (procState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
197                 continue;
198             }
199 
200             // TODO(b/191921016): use per-state CPU cluster times
201             final long[] cpuClusterTimes = null;
202 
203             boolean hasCpuFreqTimes = u.getCpuFreqTimes(result.cpuFreqTimes, uidProcState);
204             if (cpuClusterTimes != null || hasCpuFreqTimes) {
205                 result.perProcStatePowerMah[procState] += calculateUidModeledPowerMah(u,
206                         0, cpuClusterTimes, result.cpuFreqTimes);
207             }
208         }
209 
210         for (BatteryConsumer.Key key : keys) {
211             if (key.processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
212                 continue;
213             }
214 
215             final long cpuActiveTime = u.getCpuActiveTime(key.processState);
216 
217             double powerMah = result.perProcStatePowerMah[key.processState];
218             powerMah += mCpuActivePowerEstimator.calculatePower(cpuActiveTime);
219             app.setConsumedPower(key, powerMah, BatteryConsumer.POWER_MODEL_POWER_PROFILE)
220                     .setUsageDurationMillis(key, cpuActiveTime);
221         }
222     }
223 
calculatePowerAndDuration(BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel, long consumptionUC, int statsType, Result result)224     private void calculatePowerAndDuration(BatteryStats.Uid u,
225             @BatteryConsumer.PowerModel int powerModel, long consumptionUC, int statsType,
226             Result result) {
227         long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
228 
229         final double powerMah;
230         switch (powerModel) {
231             case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
232                 powerMah = uCtoMah(consumptionUC);
233                 break;
234             case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
235             default:
236                 powerMah = calculateUidModeledPowerMah(u, statsType);
237                 break;
238         }
239 
240         if (DEBUG && (durationMs != 0 || powerMah != 0)) {
241             Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + durationMs + " ms power="
242                     + BatteryStats.formatCharge(powerMah));
243         }
244 
245         // Keep track of the package with highest drain.
246         double highestDrain = 0;
247         String packageWithHighestDrain = null;
248         long durationFgMs = 0;
249         final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
250         final int processStatsCount = processStats.size();
251         for (int i = 0; i < processStatsCount; i++) {
252             final BatteryStats.Uid.Proc ps = processStats.valueAt(i);
253             final String processName = processStats.keyAt(i);
254             durationFgMs += ps.getForegroundTime(statsType);
255 
256             final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType)
257                     + ps.getForegroundTime(statsType);
258 
259             // Each App can have multiple packages and with multiple running processes.
260             // Keep track of the package who's process has the highest drain.
261             if (packageWithHighestDrain == null || packageWithHighestDrain.startsWith("*")) {
262                 highestDrain = costValue;
263                 packageWithHighestDrain = processName;
264             } else if (highestDrain < costValue && !processName.startsWith("*")) {
265                 highestDrain = costValue;
266                 packageWithHighestDrain = processName;
267             }
268         }
269 
270         // Ensure that the CPU times make sense.
271         if (durationFgMs > durationMs) {
272             if (DEBUG && durationFgMs > durationMs + 10000) {
273                 Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
274             }
275 
276             // Statistics may not have been gathered yet.
277             durationMs = durationFgMs;
278         }
279 
280         result.durationMs = durationMs;
281         result.durationFgMs = durationFgMs;
282         result.powerMah = powerMah;
283         result.packageWithHighestDrain = packageWithHighestDrain;
284     }
285 
286     /**
287      * Calculates CPU power consumed by the specified app, using the PowerProfile model.
288      */
calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType)289     public double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
290         return calculateUidModeledPowerMah(u, u.getCpuActiveTime(), u.getCpuClusterTimes(),
291                 u.getCpuFreqTimes(statsType));
292     }
293 
calculateUidModeledPowerMah(BatteryStats.Uid u, long cpuActiveTime, long[] cpuClusterTimes, long[] cpuFreqTimes)294     private double calculateUidModeledPowerMah(BatteryStats.Uid u, long cpuActiveTime,
295             long[] cpuClusterTimes, long[] cpuFreqTimes) {
296         // Constant battery drain when CPU is active
297         double powerMah = calculateActiveCpuPowerMah(cpuActiveTime);
298 
299         // Additional per-cluster battery drain
300         if (cpuClusterTimes != null) {
301             if (cpuClusterTimes.length == mNumCpuClusters) {
302                 for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
303                     final double power = mPerClusterPowerEstimators[cluster]
304                             .calculatePower(cpuClusterTimes[cluster]);
305                     powerMah += power;
306                     if (DEBUG) {
307                         Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
308                                 + " clusterTimeMs=" + cpuClusterTimes[cluster]
309                                 + " power=" + BatteryStats.formatCharge(power));
310                     }
311                 }
312             } else {
313                 Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
314                         + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
315             }
316         }
317 
318         if (cpuFreqTimes != null) {
319             if (cpuFreqTimes.length == mPerCpuFreqPowerEstimators.length) {
320                 for (int i = 0; i < cpuFreqTimes.length; i++) {
321                     powerMah += mPerCpuFreqPowerEstimators[i].calculatePower(cpuFreqTimes[i]);
322                 }
323             } else {
324                 Log.w(TAG, "UID " + u.getUid() + " CPU freq # mismatch: Power Profile # "
325                         + mPerCpuFreqPowerEstimators.length + " actual # " + cpuFreqTimes.length);
326             }
327         }
328 
329         return powerMah;
330     }
331 
332     /**
333      * Calculates active CPU power consumption.
334      *
335      * @param durationsMs duration of CPU usage.
336      * @return a double in milliamp-hours of estimated active CPU power consumption.
337      */
calculateActiveCpuPowerMah(long durationsMs)338     private double calculateActiveCpuPowerMah(long durationsMs) {
339         return mCpuActivePowerEstimator.calculatePower(durationsMs);
340     }
341 
342     /**
343      * Calculates CPU cluster power consumption.
344      *
345      * @param cluster           CPU cluster used.
346      * @param clusterDurationMs duration of CPU cluster usage.
347      * @return a double in milliamp-hours of estimated CPU cluster power consumption.
348      */
calculatePerCpuClusterPowerMah(int cluster, long clusterDurationMs)349     public double calculatePerCpuClusterPowerMah(int cluster, long clusterDurationMs) {
350         return mPerClusterPowerEstimators[cluster].calculatePower(clusterDurationMs);
351     }
352 
353     /**
354      * Calculates CPU cluster power consumption at a specific speedstep.
355      *
356      * @param cluster                 CPU cluster used.
357      * @param speedStep               which speedstep used.
358      * @param clusterSpeedDurationsMs duration of CPU cluster usage at the specified speed step.
359      * @return a double in milliamp-hours of estimated CPU cluster-speed power consumption.
360      */
calculatePerCpuFreqPowerMah(int cluster, int speedStep, long clusterSpeedDurationsMs)361     public double calculatePerCpuFreqPowerMah(int cluster, int speedStep,
362             long clusterSpeedDurationsMs) {
363         return mPerCpuFreqPowerEstimatorsByCluster[cluster][speedStep].calculatePower(
364                 clusterSpeedDurationsMs);
365     }
366 }
367