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.internal.os;
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.os.UserHandle;
24 import android.util.ArrayMap;
25 import android.util.Log;
26 import android.util.SparseArray;
27 
28 import java.util.List;
29 
30 public class CpuPowerCalculator extends PowerCalculator {
31     private static final String TAG = "CpuPowerCalculator";
32     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
33     private final int mNumCpuClusters;
34 
35     // Time-in-state based CPU power estimation model computes the estimated power
36     // by adding up three components:
37     //   - CPU Active power:    the constant amount of charge consumed by the CPU when it is on
38     //   - Per Cluster power:   the additional amount of charge consumed by a CPU cluster
39     //                          when it is running
40     //   - Per frequency power: the additional amount of charge caused by dynamic frequency scaling
41 
42     private final UsageBasedPowerEstimator mCpuActivePowerEstimator;
43     // One estimator per cluster
44     private final UsageBasedPowerEstimator[] mPerClusterPowerEstimators;
45     // Multiple estimators per cluster: one per available scaling frequency. Note that different
46     // clusters have different sets of frequencies and corresponding power consumption averages.
47     private final UsageBasedPowerEstimator[][] mPerCpuFreqPowerEstimators;
48 
49     private static class Result {
50         public long durationMs;
51         public double powerMah;
52         public long durationFgMs;
53         public String packageWithHighestDrain;
54     }
55 
CpuPowerCalculator(PowerProfile profile)56     public CpuPowerCalculator(PowerProfile profile) {
57         mNumCpuClusters = profile.getNumCpuClusters();
58 
59         mCpuActivePowerEstimator = new UsageBasedPowerEstimator(
60                 profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE));
61 
62         mPerClusterPowerEstimators = new UsageBasedPowerEstimator[mNumCpuClusters];
63         for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
64             mPerClusterPowerEstimators[cluster] = new UsageBasedPowerEstimator(
65                     profile.getAveragePowerForCpuCluster(cluster));
66         }
67 
68         mPerCpuFreqPowerEstimators = new UsageBasedPowerEstimator[mNumCpuClusters][];
69         for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
70             final int speedsForCluster = profile.getNumSpeedStepsInCpuCluster(cluster);
71             mPerCpuFreqPowerEstimators[cluster] = new UsageBasedPowerEstimator[speedsForCluster];
72             for (int speed = 0; speed < speedsForCluster; speed++) {
73                 mPerCpuFreqPowerEstimators[cluster][speed] =
74                         new UsageBasedPowerEstimator(
75                                 profile.getAveragePowerForCpuCore(cluster, speed));
76             }
77         }
78     }
79 
80     @Override
calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)81     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
82             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
83         double totalPowerMah = 0;
84 
85         Result result = new Result();
86         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
87                 builder.getUidBatteryConsumerBuilders();
88         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
89             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
90             calculateApp(app, app.getBatteryStatsUid(), query, result);
91             totalPowerMah += result.powerMah;
92         }
93 
94         final long consumptionUC = batteryStats.getCpuMeasuredBatteryConsumptionUC();
95         final int powerModel = getPowerModel(consumptionUC, query);
96 
97         builder.getAggregateBatteryConsumerBuilder(
98                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
99                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, totalPowerMah);
100         builder.getAggregateBatteryConsumerBuilder(
101                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
102                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU,
103                         powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY
104                                 ? uCtoMah(consumptionUC) : totalPowerMah, powerModel);
105     }
106 
calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, BatteryUsageStatsQuery query, Result result)107     private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
108             BatteryUsageStatsQuery query, Result result) {
109         final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC();
110         final int powerModel = getPowerModel(consumptionUC, query);
111         calculatePowerAndDuration(u, powerModel, consumptionUC, BatteryStats.STATS_SINCE_CHARGED,
112                 result);
113 
114         app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah, powerModel)
115                 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, result.durationMs)
116                 .setPackageWithHighestDrain(result.packageWithHighestDrain);
117     }
118 
119     @Override
calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers)120     public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
121             long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
122         Result result = new Result();
123         for (int i = sippers.size() - 1; i >= 0; i--) {
124             final BatterySipper app = sippers.get(i);
125             if (app.drainType == BatterySipper.DrainType.APP) {
126                 calculateApp(app, app.uidObj, statsType, result);
127             }
128         }
129     }
130 
calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result)131     private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) {
132         final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC();
133         final int powerModel = getPowerModel(consumptionUC);
134         calculatePowerAndDuration(u, powerModel, consumptionUC, statsType, result);
135 
136         app.cpuPowerMah = result.powerMah;
137         app.cpuTimeMs = result.durationMs;
138         app.cpuFgTimeMs = result.durationFgMs;
139         app.packageWithHighestDrain = result.packageWithHighestDrain;
140     }
141 
calculatePowerAndDuration(BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel, long consumptionUC, int statsType, Result result)142     private void calculatePowerAndDuration(BatteryStats.Uid u,
143             @BatteryConsumer.PowerModel int powerModel, long consumptionUC, int statsType,
144             Result result) {
145         long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
146 
147         final double powerMah;
148         switch(powerModel) {
149             case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
150                 powerMah = uCtoMah(consumptionUC);
151                 break;
152             case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
153             default:
154                 powerMah = calculateUidModeledPowerMah(u, statsType);
155                 break;
156         }
157 
158         if (DEBUG && (durationMs != 0 || powerMah != 0)) {
159             Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + durationMs + " ms power="
160                     + formatCharge(powerMah));
161         }
162 
163         // Keep track of the package with highest drain.
164         double highestDrain = 0;
165         String packageWithHighestDrain = null;
166         long durationFgMs = 0;
167         final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
168         final int processStatsCount = processStats.size();
169         for (int i = 0; i < processStatsCount; i++) {
170             final BatteryStats.Uid.Proc ps = processStats.valueAt(i);
171             final String processName = processStats.keyAt(i);
172             durationFgMs += ps.getForegroundTime(statsType);
173 
174             final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType)
175                     + ps.getForegroundTime(statsType);
176 
177             // Each App can have multiple packages and with multiple running processes.
178             // Keep track of the package who's process has the highest drain.
179             if (packageWithHighestDrain == null || packageWithHighestDrain.startsWith("*")) {
180                 highestDrain = costValue;
181                 packageWithHighestDrain = processName;
182             } else if (highestDrain < costValue && !processName.startsWith("*")) {
183                 highestDrain = costValue;
184                 packageWithHighestDrain = processName;
185             }
186         }
187 
188         // Ensure that the CPU times make sense.
189         if (durationFgMs > durationMs) {
190             if (DEBUG && durationFgMs > durationMs + 10000) {
191                 Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
192             }
193 
194             // Statistics may not have been gathered yet.
195             durationMs = durationFgMs;
196         }
197 
198         result.durationMs = durationMs;
199         result.durationFgMs = durationFgMs;
200         result.powerMah = powerMah;
201         result.packageWithHighestDrain = packageWithHighestDrain;
202     }
203 
204     /**
205      * Calculates CPU power consumed by the specified app, using the PowerProfile model.
206      */
calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType)207     public double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
208         // Constant battery drain when CPU is active
209         double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());
210 
211         // Additional per-cluster battery drain
212         long[] cpuClusterTimes = u.getCpuClusterTimes();
213         if (cpuClusterTimes != null) {
214             if (cpuClusterTimes.length == mNumCpuClusters) {
215                 for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
216                     double power = calculatePerCpuClusterPowerMah(cluster,
217                             cpuClusterTimes[cluster]);
218                     powerMah += power;
219                     if (DEBUG) {
220                         Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
221                                 + " clusterTimeMs=" + cpuClusterTimes[cluster]
222                                 + " power=" + formatCharge(power));
223                     }
224                 }
225             } else {
226                 Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
227                         + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
228             }
229         }
230 
231         // Additional per-frequency battery drain
232         for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
233             final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length;
234             for (int speed = 0; speed < speedsForCluster; speed++) {
235                 final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
236                 final double power = calculatePerCpuFreqPowerMah(cluster, speed,
237                         timeUs / 1000);
238                 if (DEBUG) {
239                     Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
240                             + speed + " timeUs=" + timeUs + " power="
241                             + formatCharge(power));
242                 }
243                 powerMah += power;
244             }
245         }
246         return powerMah;
247     }
248 
249     /**
250      * Calculates active CPU power consumption.
251      *
252      * @param durationsMs duration of CPU usage.
253      * @return a double in milliamp-hours of estimated active CPU power consumption.
254      */
calculateActiveCpuPowerMah(long durationsMs)255     public double calculateActiveCpuPowerMah(long durationsMs) {
256         return mCpuActivePowerEstimator.calculatePower(durationsMs);
257     }
258 
259     /**
260      * Calculates CPU cluster power consumption.
261      *
262      * @param cluster CPU cluster used.
263      * @param clusterDurationMs duration of CPU cluster usage.
264      * @return a double in milliamp-hours of estimated CPU cluster power consumption.
265      */
calculatePerCpuClusterPowerMah(int cluster, long clusterDurationMs)266     public double calculatePerCpuClusterPowerMah(int cluster, long clusterDurationMs) {
267         return mPerClusterPowerEstimators[cluster].calculatePower(clusterDurationMs);
268     }
269 
270     /**
271      * Calculates CPU cluster power consumption at a specific speedstep.
272      *
273      * @param cluster CPU cluster used.
274      * @param speedStep which speedstep used.
275      * @param clusterSpeedDurationsMs duration of CPU cluster usage at the specified speed step.
276      * @return a double in milliamp-hours of estimated CPU cluster-speed power consumption.
277      */
calculatePerCpuFreqPowerMah(int cluster, int speedStep, long clusterSpeedDurationsMs)278     public double calculatePerCpuFreqPowerMah(int cluster, int speedStep,
279             long clusterSpeedDurationsMs) {
280         return mPerCpuFreqPowerEstimators[cluster][speedStep].calculatePower(
281                 clusterSpeedDurationsMs);
282     }
283 }
284