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