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