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.annotation.Nullable; 19 import android.os.BatteryConsumer; 20 import android.os.BatteryStats; 21 import android.os.BatteryStats.ControllerActivityCounter; 22 import android.os.BatteryUsageStats; 23 import android.os.BatteryUsageStatsQuery; 24 import android.os.UidBatteryConsumer; 25 import android.util.Log; 26 import android.util.SparseArray; 27 28 import java.util.Arrays; 29 30 public class BluetoothPowerCalculator extends PowerCalculator { 31 private static final String TAG = "BluetoothPowerCalc"; 32 private static final boolean DEBUG = PowerCalculator.DEBUG; 33 34 private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0]; 35 36 private final double mIdleMa; 37 private final double mRxMa; 38 private final double mTxMa; 39 private final boolean mHasBluetoothPowerController; 40 41 private static class PowerAndDuration { 42 // Return value of BT duration per app 43 public long durationMs; 44 // Return value of BT power per app 45 public double powerMah; 46 47 public BatteryConsumer.Key[] keys; 48 public double[] powerPerKeyMah; 49 50 // Aggregated BT duration across all apps 51 public long totalDurationMs; 52 // Aggregated BT power across all apps 53 public double totalPowerMah; 54 } 55 BluetoothPowerCalculator(PowerProfile profile)56 public BluetoothPowerCalculator(PowerProfile profile) { 57 mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE); 58 mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX); 59 mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX); 60 mHasBluetoothPowerController = mIdleMa != 0 && mRxMa != 0 && mTxMa != 0; 61 } 62 63 @Override isPowerComponentSupported(@atteryConsumer.PowerComponent int powerComponent)64 public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) { 65 return powerComponent == BatteryConsumer.POWER_COMPONENT_BLUETOOTH; 66 } 67 68 @Override calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)69 public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, 70 long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { 71 if (!batteryStats.hasBluetoothActivityReporting()) { 72 return; 73 } 74 75 BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS; 76 final PowerAndDuration powerAndDuration = new PowerAndDuration(); 77 78 final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = 79 builder.getUidBatteryConsumerBuilders(); 80 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 81 final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); 82 if (keys == UNINITIALIZED_KEYS) { 83 if (query.isProcessStateDataNeeded()) { 84 keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_BLUETOOTH); 85 powerAndDuration.keys = keys; 86 powerAndDuration.powerPerKeyMah = new double[keys.length]; 87 } else { 88 keys = null; 89 } 90 } 91 calculateApp(app, powerAndDuration, query); 92 } 93 94 final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC(); 95 final int powerModel = getPowerModel(measuredChargeUC, query); 96 final ControllerActivityCounter activityCounter = 97 batteryStats.getBluetoothControllerActivity(); 98 calculatePowerAndDuration(null, powerModel, measuredChargeUC, 99 activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration); 100 101 // Subtract what the apps used, but clamp to 0. 102 final long systemComponentDurationMs = Math.max(0, 103 powerAndDuration.durationMs - powerAndDuration.totalDurationMs); 104 if (DEBUG) { 105 Log.d(TAG, "Bluetooth active: time=" + (systemComponentDurationMs) 106 + " power=" + BatteryStats.formatCharge(powerAndDuration.powerMah)); 107 } 108 109 builder.getAggregateBatteryConsumerBuilder( 110 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 111 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, 112 powerAndDuration.durationMs) 113 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, 114 Math.max(powerAndDuration.powerMah, powerAndDuration.totalPowerMah), 115 powerModel); 116 117 builder.getAggregateBatteryConsumerBuilder( 118 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) 119 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, 120 powerAndDuration.totalDurationMs) 121 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, 122 powerAndDuration.totalPowerMah, 123 powerModel); 124 } 125 calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration powerAndDuration, BatteryUsageStatsQuery query)126 private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration powerAndDuration, 127 BatteryUsageStatsQuery query) { 128 final long measuredChargeUC = 129 app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC(); 130 final int powerModel = getPowerModel(measuredChargeUC, query); 131 final ControllerActivityCounter activityCounter = 132 app.getBatteryStatsUid().getBluetoothControllerActivity(); 133 calculatePowerAndDuration(app.getBatteryStatsUid(), powerModel, measuredChargeUC, 134 activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration); 135 136 app.setUsageDurationMillis( 137 BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerAndDuration.durationMs) 138 .setConsumedPower( 139 BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerAndDuration.powerMah, 140 powerModel); 141 142 if (!app.isVirtualUid()) { 143 powerAndDuration.totalDurationMs += powerAndDuration.durationMs; 144 powerAndDuration.totalPowerMah += powerAndDuration.powerMah; 145 } 146 147 if (query.isProcessStateDataNeeded() && powerAndDuration.keys != null) { 148 for (int j = 0; j < powerAndDuration.keys.length; j++) { 149 BatteryConsumer.Key key = powerAndDuration.keys[j]; 150 final int processState = key.processState; 151 if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) { 152 // Already populated with the powerAndDuration across all process states 153 continue; 154 } 155 156 app.setConsumedPower(key, powerAndDuration.powerPerKeyMah[j], powerModel); 157 } 158 } 159 } 160 161 /** Returns bluetooth power usage based on the best data available. */ calculatePowerAndDuration(@ullable BatteryStats.Uid uid, @BatteryConsumer.PowerModel int powerModel, long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower, PowerAndDuration powerAndDuration)162 private void calculatePowerAndDuration(@Nullable BatteryStats.Uid uid, 163 @BatteryConsumer.PowerModel int powerModel, 164 long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower, 165 PowerAndDuration powerAndDuration) { 166 if (counter == null) { 167 powerAndDuration.durationMs = 0; 168 powerAndDuration.powerMah = 0; 169 if (powerAndDuration.powerPerKeyMah != null) { 170 Arrays.fill(powerAndDuration.powerPerKeyMah, 0); 171 } 172 return; 173 } 174 175 final BatteryStats.LongCounter idleTimeCounter = counter.getIdleTimeCounter(); 176 final BatteryStats.LongCounter rxTimeCounter = counter.getRxTimeCounter(); 177 final BatteryStats.LongCounter txTimeCounter = counter.getTxTimeCounters()[0]; 178 final long idleTimeMs = idleTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED); 179 final long rxTimeMs = rxTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED); 180 final long txTimeMs = txTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED); 181 182 powerAndDuration.durationMs = idleTimeMs + rxTimeMs + txTimeMs; 183 184 if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { 185 powerAndDuration.powerMah = uCtoMah(measuredChargeUC); 186 if (uid != null && powerAndDuration.keys != null) { 187 for (int i = 0; i < powerAndDuration.keys.length; i++) { 188 BatteryConsumer.Key key = powerAndDuration.keys[i]; 189 final int processState = key.processState; 190 if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) { 191 // Already populated with the powerAndDuration across all process states 192 continue; 193 } 194 195 powerAndDuration.powerPerKeyMah[i] = 196 uCtoMah(uid.getBluetoothMeasuredBatteryConsumptionUC(processState)); 197 } 198 } 199 } else { 200 if (!ignoreReportedPower) { 201 final double powerMah = 202 counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED) 203 / (double) (1000 * 60 * 60); 204 if (powerMah != 0) { 205 powerAndDuration.powerMah = powerMah; 206 if (powerAndDuration.powerPerKeyMah != null) { 207 // Leave this use case unsupported: used energy is reported 208 // via BluetoothActivityEnergyInfo rather than PowerStats HAL. 209 Arrays.fill(powerAndDuration.powerPerKeyMah, 0); 210 } 211 return; 212 } 213 } 214 215 if (mHasBluetoothPowerController) { 216 powerAndDuration.powerMah = calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs); 217 218 if (powerAndDuration.keys != null) { 219 for (int i = 0; i < powerAndDuration.keys.length; i++) { 220 BatteryConsumer.Key key = powerAndDuration.keys[i]; 221 final int processState = key.processState; 222 if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) { 223 // Already populated with the powerAndDuration across all process states 224 continue; 225 } 226 227 powerAndDuration.powerPerKeyMah[i] = 228 calculatePowerMah( 229 rxTimeCounter.getCountForProcessState(processState), 230 txTimeCounter.getCountForProcessState(processState), 231 idleTimeCounter.getCountForProcessState(processState)); 232 } 233 } 234 } else { 235 powerAndDuration.powerMah = 0; 236 if (powerAndDuration.powerPerKeyMah != null) { 237 Arrays.fill(powerAndDuration.powerPerKeyMah, 0); 238 } 239 } 240 } 241 } 242 243 /** Returns estimated bluetooth power usage based on usage times. */ calculatePowerMah(long rxTimeMs, long txTimeMs, long idleTimeMs)244 public double calculatePowerMah(long rxTimeMs, long txTimeMs, long idleTimeMs) { 245 return ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa)) 246 / (1000 * 60 * 60); 247 } 248 } 249