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.annotation.Nullable; 19 import android.os.BatteryConsumer; 20 import android.os.BatteryStats; 21 import android.os.BatteryUsageStats; 22 import android.os.BatteryUsageStatsQuery; 23 import android.os.UidBatteryConsumer; 24 import android.telephony.CellSignalStrength; 25 import android.telephony.ServiceState; 26 import android.util.Log; 27 import android.util.LongArrayQueue; 28 import android.util.SparseArray; 29 30 import com.android.internal.os.PowerProfile; 31 import com.android.internal.power.ModemPowerProfile; 32 33 import java.util.ArrayList; 34 35 public class MobileRadioPowerCalculator extends PowerCalculator { 36 private static final String TAG = "MobRadioPowerCalculator"; 37 private static final boolean DEBUG = PowerCalculator.DEBUG; 38 39 private static final double MILLIS_IN_HOUR = 1000.0 * 60 * 60; 40 41 private static final int NUM_SIGNAL_STRENGTH_LEVELS = 42 CellSignalStrength.getNumSignalStrengthLevels(); 43 44 private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0]; 45 private static final int IGNORE = -1; 46 47 private final UsageBasedPowerEstimator mActivePowerEstimator; // deprecated 48 private final UsageBasedPowerEstimator[] mIdlePowerEstimators = 49 new UsageBasedPowerEstimator[NUM_SIGNAL_STRENGTH_LEVELS]; // deprecated 50 private final UsageBasedPowerEstimator mScanPowerEstimator; // deprecated 51 52 @Nullable 53 private final UsageBasedPowerEstimator mSleepPowerEstimator; 54 @Nullable 55 private final UsageBasedPowerEstimator mIdlePowerEstimator; 56 57 private final PowerProfile mPowerProfile; 58 59 private static class PowerAndDuration { 60 public long remainingDurationMs; 61 public double remainingPowerMah; 62 public long totalAppDurationMs; 63 public double totalAppPowerMah; 64 } 65 MobileRadioPowerCalculator(PowerProfile profile)66 public MobileRadioPowerCalculator(PowerProfile profile) { 67 mPowerProfile = profile; 68 69 final double sleepDrainRateMa = mPowerProfile.getAverageBatteryDrainOrDefaultMa( 70 PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP, 71 Double.NaN); 72 if (Double.isNaN(sleepDrainRateMa)) { 73 mSleepPowerEstimator = null; 74 } else { 75 mSleepPowerEstimator = new UsageBasedPowerEstimator(sleepDrainRateMa); 76 } 77 78 final double idleDrainRateMa = mPowerProfile.getAverageBatteryDrainOrDefaultMa( 79 PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE, 80 Double.NaN); 81 if (Double.isNaN(idleDrainRateMa)) { 82 mIdlePowerEstimator = null; 83 } else { 84 mIdlePowerEstimator = new UsageBasedPowerEstimator(idleDrainRateMa); 85 } 86 87 // Instantiate legacy power estimators 88 double powerRadioActiveMa = 89 profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, Double.NaN); 90 if (Double.isNaN(powerRadioActiveMa)) { 91 double sum = 0; 92 sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); 93 for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { 94 sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i); 95 } 96 powerRadioActiveMa = sum / (NUM_SIGNAL_STRENGTH_LEVELS + 1); 97 } 98 mActivePowerEstimator = new UsageBasedPowerEstimator(powerRadioActiveMa); 99 100 if (!Double.isNaN( 101 profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, Double.NaN))) { 102 for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { 103 mIdlePowerEstimators[i] = new UsageBasedPowerEstimator( 104 profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i)); 105 } 106 } else { 107 double idle = profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE); 108 109 // Magical calculations preserved for historical compatibility 110 mIdlePowerEstimators[0] = new UsageBasedPowerEstimator(idle * 25 / 180); 111 for (int i = 1; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { 112 mIdlePowerEstimators[i] = new UsageBasedPowerEstimator(Math.max(1, idle / 256)); 113 } 114 } 115 116 mScanPowerEstimator = new UsageBasedPowerEstimator( 117 profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0)); 118 } 119 120 @Override isPowerComponentSupported(@atteryConsumer.PowerComponent int powerComponent)121 public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) { 122 return powerComponent == BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO; 123 } 124 125 @Override calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)126 public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, 127 long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { 128 129 PowerAndDuration total = new PowerAndDuration(); 130 131 final long totalConsumptionUC = batteryStats.getMobileRadioEnergyConsumptionUC(); 132 final int powerModel = getPowerModel(totalConsumptionUC, query); 133 134 final double totalActivePowerMah; 135 final ArrayList<UidBatteryConsumer.Builder> apps; 136 final LongArrayQueue appDurationsMs; 137 if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) { 138 // EnergyConsumer is available, don't bother calculating power. 139 totalActivePowerMah = Double.NaN; 140 apps = null; 141 appDurationsMs = null; 142 } else { 143 totalActivePowerMah = calculateActiveModemPowerMah(batteryStats, rawRealtimeUs); 144 apps = new ArrayList<>(); 145 appDurationsMs = new LongArrayQueue(); 146 } 147 148 final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = 149 builder.getUidBatteryConsumerBuilders(); 150 BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS; 151 152 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 153 final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); 154 final BatteryStats.Uid uid = app.getBatteryStatsUid(); 155 if (keys == UNINITIALIZED_KEYS) { 156 if (query.isProcessStateDataNeeded()) { 157 keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO); 158 } else { 159 keys = null; 160 } 161 } 162 163 // Sum and populate each app's active radio duration. 164 final long radioActiveDurationMs = calculateDuration(uid, 165 BatteryStats.STATS_SINCE_CHARGED); 166 if (!app.isVirtualUid()) { 167 total.totalAppDurationMs += radioActiveDurationMs; 168 } 169 app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 170 radioActiveDurationMs); 171 172 if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) { 173 // EnergyConsumer is available, populate the consumed power now. 174 final long appConsumptionUC = uid.getMobileRadioEnergyConsumptionUC(); 175 if (appConsumptionUC != BatteryStats.POWER_DATA_UNAVAILABLE) { 176 final double appConsumptionMah = uCtoMah(appConsumptionUC); 177 if (!app.isVirtualUid()) { 178 total.totalAppPowerMah += appConsumptionMah; 179 } 180 app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 181 appConsumptionMah, powerModel); 182 183 if (query.isProcessStateDataNeeded() && keys != null) { 184 for (BatteryConsumer.Key key : keys) { 185 final int processState = key.processState; 186 if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) { 187 // Already populated with the total across all process states 188 continue; 189 } 190 final long consumptionInStateUc = 191 uid.getMobileRadioEnergyConsumptionUC(processState); 192 final double powerInStateMah = uCtoMah(consumptionInStateUc); 193 app.setConsumedPower(key, powerInStateMah, powerModel); 194 } 195 } 196 } 197 } else { 198 // Cache the app and its active duration for later calculations. 199 apps.add(app); 200 appDurationsMs.addLast(radioActiveDurationMs); 201 } 202 } 203 204 long totalActiveDurationMs = batteryStats.getMobileRadioActiveTime(rawRealtimeUs, 205 BatteryStats.STATS_SINCE_CHARGED) / 1000; 206 if (totalActiveDurationMs < total.totalAppDurationMs) { 207 totalActiveDurationMs = total.totalAppDurationMs; 208 } 209 210 if (powerModel != BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) { 211 // Need to smear the calculated total active power across the apps based on app 212 // active durations. 213 final int appSize = apps.size(); 214 for (int i = 0; i < appSize; i++) { 215 final UidBatteryConsumer.Builder app = apps.get(i); 216 final long activeDurationMs = appDurationsMs.get(i); 217 218 // Proportionally attribute radio power consumption based on active duration. 219 final double appConsumptionMah; 220 if (totalActiveDurationMs == 0.0) { 221 appConsumptionMah = 0.0; 222 } else { 223 appConsumptionMah = 224 (totalActivePowerMah * activeDurationMs) / totalActiveDurationMs; 225 } 226 227 if (!app.isVirtualUid()) { 228 total.totalAppPowerMah += appConsumptionMah; 229 } 230 app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 231 appConsumptionMah, powerModel); 232 233 if (query.isProcessStateDataNeeded() && keys != null) { 234 final BatteryStats.Uid uid = app.getBatteryStatsUid(); 235 for (BatteryConsumer.Key key : keys) { 236 final int processState = key.processState; 237 if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) { 238 // Already populated with the total across all process states 239 continue; 240 } 241 242 final long durationInStateMs = 243 uid.getMobileRadioActiveTimeInProcessState(processState) / 1000; 244 // Proportionally attribute per process state radio power consumption 245 // based on time state duration. 246 final double powerInStateMah; 247 if (activeDurationMs == 0.0) { 248 powerInStateMah = 0.0; 249 } else { 250 powerInStateMah = 251 (appConsumptionMah * durationInStateMs) / activeDurationMs; 252 } 253 app.setConsumedPower(key, powerInStateMah, powerModel); 254 } 255 } 256 } 257 } 258 259 total.remainingDurationMs = totalActiveDurationMs - total.totalAppDurationMs; 260 261 // Calculate remaining power consumption. 262 if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) { 263 total.remainingPowerMah = uCtoMah(totalConsumptionUC) - total.totalAppPowerMah; 264 if (total.remainingPowerMah < 0) total.remainingPowerMah = 0; 265 } else { 266 // Smear unattributed active time and add it to the remaining power consumption. 267 if (totalActiveDurationMs != 0) { 268 total.remainingPowerMah += 269 (totalActivePowerMah * total.remainingDurationMs) / totalActiveDurationMs; 270 } 271 272 // Calculate the inactive modem power consumption. 273 final BatteryStats.ControllerActivityCounter modemActivity = 274 batteryStats.getModemControllerActivity(); 275 double inactivePowerMah = Double.NaN; 276 if (modemActivity != null) { 277 final long sleepDurationMs = modemActivity.getSleepTimeCounter().getCountLocked( 278 BatteryStats.STATS_SINCE_CHARGED); 279 final long idleDurationMs = modemActivity.getIdleTimeCounter().getCountLocked( 280 BatteryStats.STATS_SINCE_CHARGED); 281 inactivePowerMah = calcInactiveStatePowerMah(sleepDurationMs, idleDurationMs); 282 } 283 if (Double.isNaN(inactivePowerMah)) { 284 // Modem activity counters unavailable. Use legacy calculations for inactive usage. 285 final long scanningTimeMs = batteryStats.getPhoneSignalScanningTime(rawRealtimeUs, 286 BatteryStats.STATS_SINCE_CHARGED) / 1000; 287 inactivePowerMah = calcScanTimePowerMah(scanningTimeMs); 288 for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { 289 long strengthTimeMs = batteryStats.getPhoneSignalStrengthTime(i, rawRealtimeUs, 290 BatteryStats.STATS_SINCE_CHARGED) / 1000; 291 inactivePowerMah += calcIdlePowerAtSignalStrengthMah(strengthTimeMs, i); 292 } 293 } 294 if (!Double.isNaN(inactivePowerMah)) { 295 total.remainingPowerMah += inactivePowerMah; 296 } 297 298 } 299 300 if (total.remainingPowerMah != 0 || total.totalAppPowerMah != 0) { 301 builder.getAggregateBatteryConsumerBuilder( 302 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 303 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 304 total.remainingDurationMs + total.totalAppDurationMs) 305 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 306 total.remainingPowerMah + total.totalAppPowerMah, powerModel); 307 308 builder.getAggregateBatteryConsumerBuilder( 309 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) 310 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 311 total.totalAppDurationMs) 312 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 313 total.totalAppPowerMah, powerModel); 314 } 315 } 316 calculateDuration(BatteryStats.Uid u, int statsType)317 private long calculateDuration(BatteryStats.Uid u, int statsType) { 318 return u.getMobileRadioActiveTime(statsType) / 1000; 319 } 320 calculateActiveModemPowerMah(BatteryStats bs, long elapsedRealtimeUs)321 private double calculateActiveModemPowerMah(BatteryStats bs, long elapsedRealtimeUs) { 322 final long elapsedRealtimeMs = elapsedRealtimeUs / 1000; 323 final int txLvlCount = NUM_SIGNAL_STRENGTH_LEVELS; 324 double consumptionMah = 0.0; 325 326 if (DEBUG) { 327 Log.d(TAG, "Calculating radio power consumption at elapased real timestamp : " 328 + elapsedRealtimeMs + " ms"); 329 } 330 331 boolean hasConstants = false; 332 333 for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) { 334 final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR 335 ? ServiceState.FREQUENCY_RANGE_COUNT : 1; 336 for (int freq = 0; freq < freqCount; freq++) { 337 for (int txLvl = 0; txLvl < txLvlCount; txLvl++) { 338 final long txDurationMs = bs.getActiveTxRadioDurationMs(rat, freq, txLvl, 339 elapsedRealtimeMs); 340 if (txDurationMs == BatteryStats.DURATION_UNAVAILABLE) { 341 continue; 342 } 343 final double txConsumptionMah = calcTxStatePowerMah(rat, freq, txLvl, 344 txDurationMs); 345 if (Double.isNaN(txConsumptionMah)) { 346 continue; 347 } 348 hasConstants = true; 349 consumptionMah += txConsumptionMah; 350 } 351 352 final long rxDurationMs = bs.getActiveRxRadioDurationMs(rat, freq, 353 elapsedRealtimeMs); 354 if (rxDurationMs == BatteryStats.DURATION_UNAVAILABLE) { 355 continue; 356 } 357 final double rxConsumptionMah = calcRxStatePowerMah(rat, freq, rxDurationMs); 358 if (Double.isNaN(rxConsumptionMah)) { 359 continue; 360 } 361 hasConstants = true; 362 consumptionMah += rxConsumptionMah; 363 } 364 } 365 366 if (!hasConstants) { 367 final long radioActiveDurationMs = bs.getMobileRadioActiveTime(elapsedRealtimeUs, 368 BatteryStats.STATS_SINCE_CHARGED) / 1000; 369 if (DEBUG) { 370 Log.d(TAG, 371 "Failed to calculate radio power consumption. Reattempted with legacy " 372 + "method. Radio active duration : " 373 + radioActiveDurationMs + " ms"); 374 } 375 if (radioActiveDurationMs > 0) { 376 consumptionMah = calcPowerFromRadioActiveDurationMah(radioActiveDurationMs); 377 } else { 378 consumptionMah = 0.0; 379 } 380 } 381 382 if (DEBUG) { 383 Log.d(TAG, "Total active radio power consumption calculated to be " + consumptionMah 384 + " mAH."); 385 } 386 387 return consumptionMah; 388 } 389 390 /** 391 * Calculates active receive radio power consumption (in milliamp-hours) from the given state's 392 * duration. 393 */ calcRxStatePowerMah(@atteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange, long rxDurationMs)394 public double calcRxStatePowerMah(@BatteryStats.RadioAccessTechnology int rat, 395 @ServiceState.FrequencyRange int freqRange, long rxDurationMs) { 396 final long rxKey = ModemPowerProfile.getAverageBatteryDrainKey( 397 ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat, freqRange, IGNORE); 398 final double drainRateMa = mPowerProfile.getAverageBatteryDrainOrDefaultMa(rxKey, 399 Double.NaN); 400 if (Double.isNaN(drainRateMa)) { 401 Log.w(TAG, "Unavailable Power Profile constant for key 0x" + Long.toHexString(rxKey)); 402 return Double.NaN; 403 } 404 405 final double consumptionMah = drainRateMa * rxDurationMs / MILLIS_IN_HOUR; 406 if (DEBUG) { 407 Log.d(TAG, "Calculated RX consumption " + consumptionMah + " mAH from a drain rate of " 408 + drainRateMa + " mA and a duration of " + rxDurationMs + " ms for " 409 + ModemPowerProfile.keyToString((int) rxKey)); 410 } 411 return consumptionMah; 412 } 413 414 /** 415 * Calculates active transmit radio power consumption (in milliamp-hours) from the given state's 416 * duration. 417 */ calcTxStatePowerMah(@atteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange, int txLevel, long txDurationMs)418 public double calcTxStatePowerMah(@BatteryStats.RadioAccessTechnology int rat, 419 @ServiceState.FrequencyRange int freqRange, int txLevel, long txDurationMs) { 420 final long txKey = ModemPowerProfile.getAverageBatteryDrainKey( 421 ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat, freqRange, txLevel); 422 final double drainRateMa = mPowerProfile.getAverageBatteryDrainOrDefaultMa(txKey, 423 Double.NaN); 424 if (Double.isNaN(drainRateMa)) { 425 Log.w(TAG, "Unavailable Power Profile constant for key 0x" + Long.toHexString(txKey)); 426 return Double.NaN; 427 } 428 429 final double consumptionMah = drainRateMa * txDurationMs / MILLIS_IN_HOUR; 430 if (DEBUG) { 431 Log.d(TAG, "Calculated TX consumption " + consumptionMah + " mAH from a drain rate of " 432 + drainRateMa + " mA and a duration of " + txDurationMs + " ms for " 433 + ModemPowerProfile.keyToString((int) txKey)); 434 } 435 return consumptionMah; 436 } 437 438 /** 439 * Calculates active transmit radio power consumption (in milliamp-hours) from the given state's 440 * duration. 441 */ calcInactiveStatePowerMah(long sleepDurationMs, long idleDurationMs)442 public double calcInactiveStatePowerMah(long sleepDurationMs, long idleDurationMs) { 443 if (mSleepPowerEstimator == null || mIdlePowerEstimator == null) return Double.NaN; 444 final double sleepConsumptionMah = mSleepPowerEstimator.calculatePower(sleepDurationMs); 445 final double idleConsumptionMah = mIdlePowerEstimator.calculatePower(idleDurationMs); 446 if (DEBUG) { 447 Log.d(TAG, "Calculated sleep consumption " + sleepConsumptionMah 448 + " mAH from a duration of " + sleepDurationMs + " ms and idle consumption " 449 + idleConsumptionMah + " mAH from a duration of " + idleDurationMs); 450 } 451 return sleepConsumptionMah + idleConsumptionMah; 452 } 453 454 /** 455 * Calculates active radio power consumption (in milliamp-hours) from active radio duration. 456 */ calcPowerFromRadioActiveDurationMah(long radioActiveDurationMs)457 public double calcPowerFromRadioActiveDurationMah(long radioActiveDurationMs) { 458 return mActivePowerEstimator.calculatePower(radioActiveDurationMs); 459 } 460 461 /** 462 * Calculates idle radio power consumption (in milliamp-hours) for time spent at a cell signal 463 * strength level. 464 * see {@link CellSignalStrength#getNumSignalStrengthLevels()} 465 */ calcIdlePowerAtSignalStrengthMah(long strengthTimeMs, int strengthLevel)466 public double calcIdlePowerAtSignalStrengthMah(long strengthTimeMs, int strengthLevel) { 467 return mIdlePowerEstimators[strengthLevel].calculatePower(strengthTimeMs); 468 } 469 470 /** 471 * Calculates radio scan power consumption (in milliamp-hours) from scan time. 472 */ calcScanTimePowerMah(long scanningTimeMs)473 public double calcScanTimePowerMah(long scanningTimeMs) { 474 return mScanPowerEstimator.calculatePower(scanningTimeMs); 475 } 476 } 477