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.telephony.CellSignalStrength; 24 import android.util.Log; 25 import android.util.SparseArray; 26 27 public class MobileRadioPowerCalculator extends PowerCalculator { 28 private static final String TAG = "MobRadioPowerCalculator"; 29 private static final boolean DEBUG = PowerCalculator.DEBUG; 30 31 private static final int NUM_SIGNAL_STRENGTH_LEVELS = 32 CellSignalStrength.getNumSignalStrengthLevels(); 33 34 private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0]; 35 36 private final UsageBasedPowerEstimator mActivePowerEstimator; 37 private final UsageBasedPowerEstimator[] mIdlePowerEstimators = 38 new UsageBasedPowerEstimator[NUM_SIGNAL_STRENGTH_LEVELS]; 39 private final UsageBasedPowerEstimator mScanPowerEstimator; 40 41 private static class PowerAndDuration { 42 public long durationMs; 43 public double remainingPowerMah; 44 public long totalAppDurationMs; 45 public double totalAppPowerMah; 46 public long signalDurationMs; 47 public long noCoverageDurationMs; 48 } 49 MobileRadioPowerCalculator(PowerProfile profile)50 public MobileRadioPowerCalculator(PowerProfile profile) { 51 // Power consumption when radio is active 52 double powerRadioActiveMa = 53 profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, -1); 54 if (powerRadioActiveMa == -1) { 55 double sum = 0; 56 sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); 57 for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { 58 sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i); 59 } 60 powerRadioActiveMa = sum / (NUM_SIGNAL_STRENGTH_LEVELS + 1); 61 } 62 63 mActivePowerEstimator = new UsageBasedPowerEstimator(powerRadioActiveMa); 64 65 // Power consumption when radio is on, but idle 66 if (profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, -1) != -1) { 67 for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { 68 mIdlePowerEstimators[i] = new UsageBasedPowerEstimator( 69 profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i)); 70 } 71 } else { 72 double idle = profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE); 73 74 // Magical calculations preserved for historical compatibility 75 mIdlePowerEstimators[0] = new UsageBasedPowerEstimator(idle * 25 / 180); 76 for (int i = 1; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { 77 mIdlePowerEstimators[i] = new UsageBasedPowerEstimator(Math.max(1, idle / 256)); 78 } 79 } 80 81 mScanPowerEstimator = new UsageBasedPowerEstimator( 82 profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0)); 83 } 84 85 @Override isPowerComponentSupported(@atteryConsumer.PowerComponent int powerComponent)86 public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) { 87 return powerComponent == BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO; 88 } 89 90 @Override calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)91 public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, 92 long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { 93 94 PowerAndDuration total = new PowerAndDuration(); 95 96 final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = 97 builder.getUidBatteryConsumerBuilders(); 98 BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS; 99 100 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 101 final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); 102 final BatteryStats.Uid uid = app.getBatteryStatsUid(); 103 if (keys == UNINITIALIZED_KEYS) { 104 if (query.isProcessStateDataNeeded()) { 105 keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO); 106 } else { 107 keys = null; 108 } 109 } 110 111 calculateApp(app, uid, total, query, keys); 112 } 113 114 final long totalConsumptionUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC(); 115 final int powerModel = getPowerModel(totalConsumptionUC, query); 116 calculateRemaining(total, powerModel, batteryStats, rawRealtimeUs, totalConsumptionUC); 117 118 if (total.remainingPowerMah != 0 || total.totalAppPowerMah != 0) { 119 builder.getAggregateBatteryConsumerBuilder( 120 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 121 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 122 total.durationMs) 123 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 124 total.remainingPowerMah + total.totalAppPowerMah, powerModel); 125 126 builder.getAggregateBatteryConsumerBuilder( 127 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) 128 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 129 total.durationMs) 130 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 131 total.totalAppPowerMah, powerModel); 132 } 133 } 134 calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, PowerAndDuration total, BatteryUsageStatsQuery query, BatteryConsumer.Key[] keys)135 private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, 136 PowerAndDuration total, 137 BatteryUsageStatsQuery query, BatteryConsumer.Key[] keys) { 138 final long radioActiveDurationMs = calculateDuration(u, BatteryStats.STATS_SINCE_CHARGED); 139 final long consumptionUC = u.getMobileRadioMeasuredBatteryConsumptionUC(); 140 final int powerModel = getPowerModel(consumptionUC, query); 141 final double powerMah = calculatePower(u, powerModel, radioActiveDurationMs, consumptionUC); 142 143 if (!app.isVirtualUid()) { 144 total.totalAppDurationMs += radioActiveDurationMs; 145 total.totalAppPowerMah += powerMah; 146 } 147 148 app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 149 radioActiveDurationMs) 150 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, powerMah, 151 powerModel); 152 153 if (query.isProcessStateDataNeeded() && keys != null) { 154 for (BatteryConsumer.Key key: keys) { 155 final int processState = key.processState; 156 if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) { 157 // Already populated with the total across all process states 158 continue; 159 } 160 161 final long durationInStateMs = 162 u.getMobileRadioActiveTimeInProcessState(processState) / 1000; 163 final long consumptionInStateUc = 164 u.getMobileRadioMeasuredBatteryConsumptionUC(processState); 165 final double powerInStateMah = calculatePower(u, powerModel, durationInStateMs, 166 consumptionInStateUc); 167 app.setConsumedPower(key, powerInStateMah, powerModel); 168 } 169 } 170 } 171 calculateDuration(BatteryStats.Uid u, int statsType)172 private long calculateDuration(BatteryStats.Uid u, int statsType) { 173 return u.getMobileRadioActiveTime(statsType) / 1000; 174 } 175 calculatePower(BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel, long radioActiveDurationMs, long measuredChargeUC)176 private double calculatePower(BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel, 177 long radioActiveDurationMs, long measuredChargeUC) { 178 if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { 179 return uCtoMah(measuredChargeUC); 180 } 181 182 if (radioActiveDurationMs > 0) { 183 return calcPowerFromRadioActiveDurationMah(radioActiveDurationMs); 184 } 185 return 0; 186 } 187 calculateRemaining(PowerAndDuration total, @BatteryConsumer.PowerModel int powerModel, BatteryStats batteryStats, long rawRealtimeUs, long totalConsumptionUC)188 private void calculateRemaining(PowerAndDuration total, 189 @BatteryConsumer.PowerModel int powerModel, BatteryStats batteryStats, 190 long rawRealtimeUs, long totalConsumptionUC) { 191 long signalTimeMs = 0; 192 double powerMah = 0; 193 194 if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { 195 powerMah = uCtoMah(totalConsumptionUC) - total.totalAppPowerMah; 196 if (powerMah < 0) powerMah = 0; 197 } 198 199 for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { 200 long strengthTimeMs = batteryStats.getPhoneSignalStrengthTime(i, rawRealtimeUs, 201 BatteryStats.STATS_SINCE_CHARGED) / 1000; 202 if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) { 203 final double p = calcIdlePowerAtSignalStrengthMah(strengthTimeMs, i); 204 if (DEBUG && p != 0) { 205 Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power=" 206 + BatteryStats.formatCharge(p)); 207 } 208 powerMah += p; 209 } 210 signalTimeMs += strengthTimeMs; 211 if (i == 0) { 212 total.noCoverageDurationMs = strengthTimeMs; 213 } 214 } 215 216 final long scanningTimeMs = batteryStats.getPhoneSignalScanningTime(rawRealtimeUs, 217 BatteryStats.STATS_SINCE_CHARGED) / 1000; 218 long radioActiveTimeMs = batteryStats.getMobileRadioActiveTime(rawRealtimeUs, 219 BatteryStats.STATS_SINCE_CHARGED) / 1000; 220 long remainingActiveTimeMs = radioActiveTimeMs - total.totalAppDurationMs; 221 222 if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) { 223 final double p = calcScanTimePowerMah(scanningTimeMs); 224 if (DEBUG && p != 0) { 225 Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs 226 + " power=" + BatteryStats.formatCharge(p)); 227 } 228 powerMah += p; 229 230 if (remainingActiveTimeMs > 0) { 231 powerMah += calcPowerFromRadioActiveDurationMah(remainingActiveTimeMs); 232 } 233 } 234 total.durationMs = radioActiveTimeMs; 235 total.remainingPowerMah = powerMah; 236 total.signalDurationMs = signalTimeMs; 237 } 238 239 /** 240 * Calculates active radio power consumption (in milliamp-hours) from active radio duration. 241 */ calcPowerFromRadioActiveDurationMah(long radioActiveDurationMs)242 public double calcPowerFromRadioActiveDurationMah(long radioActiveDurationMs) { 243 return mActivePowerEstimator.calculatePower(radioActiveDurationMs); 244 } 245 246 /** 247 * Calculates idle radio power consumption (in milliamp-hours) for time spent at a cell signal 248 * strength level. 249 * see {@link CellSignalStrength#getNumSignalStrengthLevels()} 250 */ calcIdlePowerAtSignalStrengthMah(long strengthTimeMs, int strengthLevel)251 public double calcIdlePowerAtSignalStrengthMah(long strengthTimeMs, int strengthLevel) { 252 return mIdlePowerEstimators[strengthLevel].calculatePower(strengthTimeMs); 253 } 254 255 /** 256 * Calculates radio scan power consumption (in milliamp-hours) from scan time. 257 */ calcScanTimePowerMah(long scanningTimeMs)258 public double calcScanTimePowerMah(long scanningTimeMs) { 259 return mScanPowerEstimator.calculatePower(scanningTimeMs); 260 } 261 } 262