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.BatteryStats.ControllerActivityCounter; 21 import android.os.BatteryUsageStats; 22 import android.os.BatteryUsageStatsQuery; 23 import android.os.Process; 24 import android.os.UidBatteryConsumer; 25 import android.os.UserHandle; 26 import android.util.Log; 27 import android.util.SparseArray; 28 29 import java.util.List; 30 31 public class BluetoothPowerCalculator extends PowerCalculator { 32 private static final String TAG = "BluetoothPowerCalc"; 33 private static final boolean DEBUG = BatteryStatsHelper.DEBUG; 34 private final double mIdleMa; 35 private final double mRxMa; 36 private final double mTxMa; 37 private final boolean mHasBluetoothPowerController; 38 39 private static class PowerAndDuration { 40 public long durationMs; 41 public double powerMah; 42 } 43 BluetoothPowerCalculator(PowerProfile profile)44 public BluetoothPowerCalculator(PowerProfile profile) { 45 mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE); 46 mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX); 47 mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX); 48 mHasBluetoothPowerController = mIdleMa != 0 && mRxMa != 0 && mTxMa != 0; 49 } 50 51 @Override calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)52 public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, 53 long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { 54 if (!batteryStats.hasBluetoothActivityReporting()) { 55 return; 56 } 57 58 final PowerAndDuration total = new PowerAndDuration(); 59 60 final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = 61 builder.getUidBatteryConsumerBuilders(); 62 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 63 final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); 64 calculateApp(app, total, query); 65 } 66 67 final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC(); 68 final int powerModel = getPowerModel(measuredChargeUC, query); 69 final ControllerActivityCounter activityCounter = 70 batteryStats.getBluetoothControllerActivity(); 71 final long systemDurationMs = calculateDuration(activityCounter); 72 final double systemPowerMah = calculatePowerMah(powerModel, measuredChargeUC, 73 activityCounter, query.shouldForceUsePowerProfileModel()); 74 75 // Subtract what the apps used, but clamp to 0. 76 final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs); 77 if (DEBUG) { 78 Log.d(TAG, "Bluetooth active: time=" + (systemComponentDurationMs) 79 + " power=" + formatCharge(systemPowerMah)); 80 } 81 82 builder.getAggregateBatteryConsumerBuilder( 83 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 84 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, systemDurationMs) 85 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, 86 Math.max(systemPowerMah, total.powerMah), powerModel); 87 88 builder.getAggregateBatteryConsumerBuilder( 89 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) 90 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.durationMs) 91 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.powerMah, 92 powerModel); 93 } 94 calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total, BatteryUsageStatsQuery query)95 private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total, 96 BatteryUsageStatsQuery query) { 97 final long measuredChargeUC = 98 app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC(); 99 final int powerModel = getPowerModel(measuredChargeUC, query); 100 final ControllerActivityCounter activityCounter = 101 app.getBatteryStatsUid().getBluetoothControllerActivity(); 102 final long durationMs = calculateDuration(activityCounter); 103 final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter, 104 query.shouldForceUsePowerProfileModel()); 105 106 app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, durationMs) 107 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah, powerModel); 108 109 total.durationMs += durationMs; 110 total.powerMah += powerMah; 111 } 112 113 @Override calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers)114 public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, 115 long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { 116 if (!mHasBluetoothPowerController || !batteryStats.hasBluetoothActivityReporting()) { 117 return; 118 } 119 120 PowerAndDuration total = new PowerAndDuration(); 121 122 for (int i = sippers.size() - 1; i >= 0; i--) { 123 final BatterySipper app = sippers.get(i); 124 if (app.drainType == BatterySipper.DrainType.APP) { 125 calculateApp(app, app.uidObj, statsType, total); 126 } 127 } 128 129 BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0); 130 final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC(); 131 final int powerModel = getPowerModel(measuredChargeUC); 132 final ControllerActivityCounter activityCounter = 133 batteryStats.getBluetoothControllerActivity(); 134 final long systemDurationMs = calculateDuration(activityCounter); 135 final double systemPowerMah = 136 calculatePowerMah(powerModel, measuredChargeUC, activityCounter, false); 137 138 // Subtract what the apps used, but clamp to 0. 139 final double powerMah = Math.max(0, systemPowerMah - total.powerMah); 140 final long durationMs = Math.max(0, systemDurationMs - total.durationMs); 141 if (DEBUG && powerMah != 0) { 142 Log.d(TAG, "Bluetooth active: time=" + (durationMs) 143 + " power=" + formatCharge(powerMah)); 144 } 145 146 bs.bluetoothPowerMah = powerMah; 147 bs.bluetoothRunningTimeMs = durationMs; 148 149 for (int i = sippers.size() - 1; i >= 0; i--) { 150 BatterySipper app = sippers.get(i); 151 if (app.getUid() == Process.BLUETOOTH_UID) { 152 if (DEBUG) Log.d(TAG, "Bluetooth adding sipper " + app + ": cpu=" + app.cpuTimeMs); 153 app.isAggregated = true; 154 bs.add(app); 155 } 156 } 157 if (bs.sumPower() > 0) { 158 sippers.add(bs); 159 } 160 } 161 calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, PowerAndDuration total)162 private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, 163 PowerAndDuration total) { 164 165 final long measuredChargeUC = u.getBluetoothMeasuredBatteryConsumptionUC(); 166 final int powerModel = getPowerModel(measuredChargeUC); 167 final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity(); 168 final long durationMs = calculateDuration(activityCounter); 169 final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter, 170 false); 171 172 app.bluetoothRunningTimeMs = durationMs; 173 app.bluetoothPowerMah = powerMah; 174 app.btRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_RX_DATA, statsType); 175 app.btTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_TX_DATA, statsType); 176 177 total.durationMs += durationMs; 178 total.powerMah += powerMah; 179 } 180 calculateDuration(ControllerActivityCounter counter)181 private long calculateDuration(ControllerActivityCounter counter) { 182 if (counter == null) { 183 return 0; 184 } 185 186 return counter.getIdleTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED) 187 + counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED) 188 + counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED); 189 } 190 191 /** Returns bluetooth power usage based on the best data available. */ calculatePowerMah(@atteryConsumer.PowerModel int powerModel, long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower)192 private double calculatePowerMah(@BatteryConsumer.PowerModel int powerModel, 193 long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower) { 194 if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { 195 return uCtoMah(measuredChargeUC); 196 } 197 198 if (counter == null) { 199 return 0; 200 } 201 202 if (!ignoreReportedPower) { 203 final double powerMah = 204 counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED) 205 / (double) (1000 * 60 * 60); 206 if (powerMah != 0) { 207 return powerMah; 208 } 209 } 210 211 if (!mHasBluetoothPowerController) { 212 return 0; 213 } 214 215 final long idleTimeMs = 216 counter.getIdleTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED); 217 final long rxTimeMs = 218 counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED); 219 final long txTimeMs = 220 counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED); 221 return calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs); 222 } 223 224 /** Returns estimated bluetooth power usage based on usage times. */ calculatePowerMah(long rxTimeMs, long txTimeMs, long idleTimeMs)225 public double calculatePowerMah(long rxTimeMs, long txTimeMs, long idleTimeMs) { 226 return ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa)) 227 / (1000 * 60 * 60); 228 } 229 } 230