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