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