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.Log;
24 import android.util.SparseArray;
25 
26 import com.android.internal.os.PowerProfile;
27 
28 import java.util.Arrays;
29 
30 /**
31  * WiFi power calculator for when BatteryStats supports energy reporting
32  * from the WiFi controller.
33  */
34 public class WifiPowerCalculator extends PowerCalculator {
35     private static final boolean DEBUG = PowerCalculator.DEBUG;
36     private static final String TAG = "WifiPowerCalculator";
37 
38     private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
39 
40     private final UsageBasedPowerEstimator mIdlePowerEstimator;
41     private final UsageBasedPowerEstimator mTxPowerEstimator;
42     private final UsageBasedPowerEstimator mRxPowerEstimator;
43     private final UsageBasedPowerEstimator mPowerOnPowerEstimator;
44     private final UsageBasedPowerEstimator mScanPowerEstimator;
45     private final UsageBasedPowerEstimator mBatchScanPowerEstimator;
46     private final boolean mHasWifiPowerController;
47     private final double mWifiPowerPerPacket;
48 
49     private static class PowerDurationAndTraffic {
50         public double powerMah;
51         public long durationMs;
52 
53         public long wifiRxPackets;
54         public long wifiTxPackets;
55         public long wifiRxBytes;
56         public long wifiTxBytes;
57 
58         public BatteryConsumer.Key[] keys;
59         public double[] powerPerKeyMah;
60     }
61 
WifiPowerCalculator(PowerProfile profile)62     public WifiPowerCalculator(PowerProfile profile) {
63         mPowerOnPowerEstimator = new UsageBasedPowerEstimator(
64                 profile.getAveragePower(PowerProfile.POWER_WIFI_ON));
65         mScanPowerEstimator = new UsageBasedPowerEstimator(
66                 profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN));
67         mBatchScanPowerEstimator = new UsageBasedPowerEstimator(
68                 profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN));
69         mIdlePowerEstimator = new UsageBasedPowerEstimator(
70                 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE));
71         mTxPowerEstimator = new UsageBasedPowerEstimator(
72                 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX));
73         mRxPowerEstimator = new UsageBasedPowerEstimator(
74                 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX));
75 
76         mWifiPowerPerPacket = getWifiPowerPerPacket(profile);
77 
78         mHasWifiPowerController =
79                 mIdlePowerEstimator.isSupported() && mTxPowerEstimator.isSupported()
80                         && mRxPowerEstimator.isSupported();
81     }
82 
83     @Override
isPowerComponentSupported(@atteryConsumer.PowerComponent int powerComponent)84     public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) {
85         return powerComponent == BatteryConsumer.POWER_COMPONENT_WIFI;
86     }
87 
88     @Override
calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)89     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
90             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
91         BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
92         long totalAppDurationMs = 0;
93         double totalAppPowerMah = 0;
94         final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic();
95         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
96                 builder.getUidBatteryConsumerBuilders();
97         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
98             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
99             if (keys == UNINITIALIZED_KEYS) {
100                 if (query.isProcessStateDataNeeded()) {
101                     keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_WIFI);
102                     powerDurationAndTraffic.keys = keys;
103                     powerDurationAndTraffic.powerPerKeyMah = new double[keys.length];
104                 } else {
105                     keys = null;
106                 }
107             }
108 
109             final long consumptionUC =
110                     app.getBatteryStatsUid().getWifiEnergyConsumptionUC();
111             final int powerModel = getPowerModel(consumptionUC, query);
112 
113             calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), powerModel,
114                     rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED,
115                     batteryStats.hasWifiActivityReporting(), consumptionUC);
116             if (!app.isVirtualUid()) {
117                 totalAppDurationMs += powerDurationAndTraffic.durationMs;
118                 totalAppPowerMah += powerDurationAndTraffic.powerMah;
119             }
120 
121             app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI,
122                     powerDurationAndTraffic.durationMs);
123             app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
124                     powerDurationAndTraffic.powerMah, powerModel);
125 
126             if (query.isProcessStateDataNeeded() && keys != null) {
127                 for (int j = 0; j < keys.length; j++) {
128                     BatteryConsumer.Key key = keys[j];
129                     final int processState = key.processState;
130                     if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
131                         // Already populated with the total across all process states
132                         continue;
133                     }
134 
135                     app.setConsumedPower(key, powerDurationAndTraffic.powerPerKeyMah[j],
136                             powerModel);
137                 }
138             }
139         }
140 
141         final long consumptionUC = batteryStats.getWifiEnergyConsumptionUC();
142         final int powerModel = getPowerModel(consumptionUC, query);
143         calculateRemaining(powerDurationAndTraffic, powerModel, batteryStats, rawRealtimeUs,
144                 BatteryStats.STATS_SINCE_CHARGED, batteryStats.hasWifiActivityReporting(),
145                 totalAppDurationMs, totalAppPowerMah, consumptionUC);
146 
147         builder.getAggregateBatteryConsumerBuilder(
148                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
149                 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI,
150                         powerDurationAndTraffic.durationMs)
151                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
152                         totalAppPowerMah + powerDurationAndTraffic.powerMah, powerModel);
153         builder.getAggregateBatteryConsumerBuilder(
154                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
155                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
156                         totalAppPowerMah, powerModel);
157     }
158 
calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel, long rawRealtimeUs, int statsType, boolean hasWifiActivityReporting, long consumptionUC)159     private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic,
160             BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel,
161             long rawRealtimeUs, int statsType, boolean hasWifiActivityReporting,
162             long consumptionUC) {
163 
164         powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets(
165                 BatteryStats.NETWORK_WIFI_RX_DATA,
166                 statsType);
167         powerDurationAndTraffic.wifiTxPackets = u.getNetworkActivityPackets(
168                 BatteryStats.NETWORK_WIFI_TX_DATA,
169                 statsType);
170         powerDurationAndTraffic.wifiRxBytes = u.getNetworkActivityBytes(
171                 BatteryStats.NETWORK_WIFI_RX_DATA,
172                 statsType);
173         powerDurationAndTraffic.wifiTxBytes = u.getNetworkActivityBytes(
174                 BatteryStats.NETWORK_WIFI_TX_DATA,
175                 statsType);
176 
177         if (hasWifiActivityReporting && mHasWifiPowerController) {
178             final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
179             if (counter != null) {
180                 final BatteryStats.LongCounter rxTimeCounter = counter.getRxTimeCounter();
181                 final BatteryStats.LongCounter txTimeCounter = counter.getTxTimeCounters()[0];
182                 final BatteryStats.LongCounter idleTimeCounter = counter.getIdleTimeCounter();
183 
184                 final long rxTime = rxTimeCounter.getCountLocked(statsType);
185                 final long txTime = txTimeCounter.getCountLocked(statsType);
186                 final long idleTime = idleTimeCounter.getCountLocked(statsType);
187 
188                 powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime;
189                 if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
190                     powerDurationAndTraffic.powerMah =
191                             calcPowerFromControllerDataMah(rxTime, txTime, idleTime);
192                 } else {
193                     powerDurationAndTraffic.powerMah = uCtoMah(consumptionUC);
194                 }
195 
196                 if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
197                     Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime
198                             + "ms tx=" + txTime + "ms power=" + BatteryStats.formatCharge(
199                             powerDurationAndTraffic.powerMah));
200                 }
201 
202                 if (powerDurationAndTraffic.keys != null) {
203                     for (int i = 0; i < powerDurationAndTraffic.keys.length; i++) {
204                         final int processState = powerDurationAndTraffic.keys[i].processState;
205                         if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
206                             continue;
207                         }
208 
209                         if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
210                             powerDurationAndTraffic.powerPerKeyMah[i] =
211                                     calcPowerFromControllerDataMah(
212                                             rxTimeCounter.getCountForProcessState(processState),
213                                             txTimeCounter.getCountForProcessState(processState),
214                                             idleTimeCounter.getCountForProcessState(processState));
215                         } else {
216                             powerDurationAndTraffic.powerPerKeyMah[i] =
217                                     uCtoMah(u.getWifiEnergyConsumptionUC(processState));
218                         }
219                     }
220                 }
221             } else {
222                 powerDurationAndTraffic.durationMs = 0;
223                 powerDurationAndTraffic.powerMah = 0;
224                 if (powerDurationAndTraffic.powerPerKeyMah != null) {
225                     Arrays.fill(powerDurationAndTraffic.powerPerKeyMah, 0);
226                 }
227             }
228         } else {
229             final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
230             powerDurationAndTraffic.durationMs = wifiRunningTime;
231 
232             if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
233                 final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
234                 long batchTimeMs = 0;
235                 for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
236                     batchTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
237                 }
238                 powerDurationAndTraffic.powerMah = calcPowerWithoutControllerDataMah(
239                         powerDurationAndTraffic.wifiRxPackets,
240                         powerDurationAndTraffic.wifiTxPackets,
241                         wifiRunningTime, wifiScanTimeMs, batchTimeMs);
242             } else {
243                 powerDurationAndTraffic.powerMah = uCtoMah(consumptionUC);
244             }
245 
246             if (powerDurationAndTraffic.powerPerKeyMah != null) {
247                 // Per-process state attribution is not supported in the absence of WiFi
248                 // activity reporting
249                 Arrays.fill(powerDurationAndTraffic.powerPerKeyMah, 0);
250             }
251 
252             if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
253                 Log.d(TAG, "UID " + u.getUid() + ": power=" + BatteryStats.formatCharge(
254                         powerDurationAndTraffic.powerMah));
255             }
256         }
257     }
258 
calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic, @BatteryConsumer.PowerModel int powerModel, BatteryStats stats, long rawRealtimeUs, int statsType, boolean hasWifiActivityReporting, long totalAppDurationMs, double totalAppPowerMah, long consumptionUC)259     private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic,
260             @BatteryConsumer.PowerModel int powerModel, BatteryStats stats, long rawRealtimeUs,
261             int statsType, boolean hasWifiActivityReporting, long totalAppDurationMs,
262             double totalAppPowerMah, long consumptionUC) {
263 
264         long totalDurationMs;
265         double totalPowerMah = 0;
266 
267         if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) {
268             totalPowerMah = uCtoMah(consumptionUC);
269         }
270 
271         if (hasWifiActivityReporting && mHasWifiPowerController) {
272             final BatteryStats.ControllerActivityCounter counter =
273                     stats.getWifiControllerActivity();
274 
275             final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
276             final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
277             final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
278 
279             totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs;
280 
281             if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
282                 totalPowerMah = counter.getPowerCounter().getCountLocked(statsType)
283                         / (double) (1000 * 60 * 60);
284                 if (totalPowerMah == 0) {
285                     // Some controllers do not report power drain, so we can calculate it here.
286                     totalPowerMah = calcPowerFromControllerDataMah(rxTimeMs, txTimeMs, idleTimeMs);
287                 }
288             }
289         } else {
290             totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000;
291             if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
292                 totalPowerMah = calcGlobalPowerWithoutControllerDataMah(totalDurationMs);
293             }
294         }
295 
296         powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs);
297         powerDurationAndTraffic.powerMah = Math.max(0, totalPowerMah - totalAppPowerMah);
298 
299         if (DEBUG) {
300             Log.d(TAG, "left over WiFi power: " + BatteryStats.formatCharge(
301                     powerDurationAndTraffic.powerMah));
302         }
303     }
304 
305     /** Returns (global or uid) estimated wifi power used using WifiControllerActivity data. */
calcPowerFromControllerDataMah(long rxTimeMs, long txTimeMs, long idleTimeMs)306     public double calcPowerFromControllerDataMah(long rxTimeMs, long txTimeMs, long idleTimeMs) {
307         return mRxPowerEstimator.calculatePower(rxTimeMs)
308                 + mTxPowerEstimator.calculatePower(txTimeMs)
309                 + mIdlePowerEstimator.calculatePower(idleTimeMs);
310     }
311 
312     /** Returns per-uid estimated wifi power used using non-WifiControllerActivity data. */
calcPowerWithoutControllerDataMah(long rxPackets, long txPackets, long wifiRunningTimeMs, long wifiScanTimeMs, long wifiBatchScanTimeMs)313     public double calcPowerWithoutControllerDataMah(long rxPackets, long txPackets,
314             long wifiRunningTimeMs, long wifiScanTimeMs, long wifiBatchScanTimeMs) {
315         return
316                 (rxPackets + txPackets) * mWifiPowerPerPacket
317                 + mPowerOnPowerEstimator.calculatePower(wifiRunningTimeMs)
318                 + mScanPowerEstimator.calculatePower(wifiScanTimeMs)
319                 + mBatchScanPowerEstimator.calculatePower(wifiBatchScanTimeMs);
320 
321     }
322 
323     /** Returns global estimated wifi power used using non-WifiControllerActivity data. */
calcGlobalPowerWithoutControllerDataMah(long globalWifiRunningTimeMs)324     public double calcGlobalPowerWithoutControllerDataMah(long globalWifiRunningTimeMs) {
325         return mPowerOnPowerEstimator.calculatePower(globalWifiRunningTimeMs);
326     }
327 
328     /**
329      * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB.
330      */
getWifiPowerPerPacket(PowerProfile profile)331     private static double getWifiPowerPerPacket(PowerProfile profile) {
332         // TODO(b/179392913): Extract average bit rates from system
333         final long wifiBps = 1000000;
334         final double averageWifiActivePower =
335                 profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) / 3600;
336         return averageWifiActivePower / (((double) wifiBps) / 8 / 2048);
337     }
338 }
339