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