blob: c322258eda85a4d4585f10a1cfd982d49e3432d9 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.os;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryStats.ControllerActivityCounter;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
import java.util.List;
public class BluetoothPowerCalculator extends PowerCalculator {
private static final String TAG = "BluetoothPowerCalc";
private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
private final double mIdleMa;
private final double mRxMa;
private final double mTxMa;
private final boolean mHasBluetoothPowerController;
private static class PowerAndDuration {
public long durationMs;
public double powerMah;
}
public BluetoothPowerCalculator(PowerProfile profile) {
mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE);
mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX);
mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX);
mHasBluetoothPowerController = mIdleMa != 0 && mRxMa != 0 && mTxMa != 0;
}
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
if (!batteryStats.hasBluetoothActivityReporting()) {
return;
}
final PowerAndDuration total = new PowerAndDuration();
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
calculateApp(app, total, query);
}
final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(measuredChargeUC, query);
final ControllerActivityCounter activityCounter =
batteryStats.getBluetoothControllerActivity();
final long systemDurationMs = calculateDuration(activityCounter);
final double systemPowerMah = calculatePowerMah(powerModel, measuredChargeUC,
activityCounter, query.shouldForceUsePowerProfileModel());
// Subtract what the apps used, but clamp to 0.
final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs);
if (DEBUG) {
Log.d(TAG, "Bluetooth active: time=" + (systemComponentDurationMs)
+ " power=" + formatCharge(systemPowerMah));
}
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, systemDurationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
Math.max(systemPowerMah, total.powerMah), powerModel);
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.powerMah,
powerModel);
}
private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total,
BatteryUsageStatsQuery query) {
final long measuredChargeUC =
app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(measuredChargeUC, query);
final ControllerActivityCounter activityCounter =
app.getBatteryStatsUid().getBluetoothControllerActivity();
final long durationMs = calculateDuration(activityCounter);
final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter,
query.shouldForceUsePowerProfileModel());
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah, powerModel);
total.durationMs += durationMs;
total.powerMah += powerMah;
}
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
if (!mHasBluetoothPowerController || !batteryStats.hasBluetoothActivityReporting()) {
return;
}
PowerAndDuration total = new PowerAndDuration();
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper app = sippers.get(i);
if (app.drainType == BatterySipper.DrainType.APP) {
calculateApp(app, app.uidObj, statsType, total);
}
}
BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(measuredChargeUC);
final ControllerActivityCounter activityCounter =
batteryStats.getBluetoothControllerActivity();
final long systemDurationMs = calculateDuration(activityCounter);
final double systemPowerMah =
calculatePowerMah(powerModel, measuredChargeUC, activityCounter, false);
// Subtract what the apps used, but clamp to 0.
final double powerMah = Math.max(0, systemPowerMah - total.powerMah);
final long durationMs = Math.max(0, systemDurationMs - total.durationMs);
if (DEBUG && powerMah != 0) {
Log.d(TAG, "Bluetooth active: time=" + (durationMs)
+ " power=" + formatCharge(powerMah));
}
bs.bluetoothPowerMah = powerMah;
bs.bluetoothRunningTimeMs = durationMs;
for (int i = sippers.size() - 1; i >= 0; i--) {
BatterySipper app = sippers.get(i);
if (app.getUid() == Process.BLUETOOTH_UID) {
if (DEBUG) Log.d(TAG, "Bluetooth adding sipper " + app + ": cpu=" + app.cpuTimeMs);
app.isAggregated = true;
bs.add(app);
}
}
if (bs.sumPower() > 0) {
sippers.add(bs);
}
}
private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
PowerAndDuration total) {
final long measuredChargeUC = u.getBluetoothMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(measuredChargeUC);
final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity();
final long durationMs = calculateDuration(activityCounter);
final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter,
false);
app.bluetoothRunningTimeMs = durationMs;
app.bluetoothPowerMah = powerMah;
app.btRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_RX_DATA, statsType);
app.btTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_TX_DATA, statsType);
total.durationMs += durationMs;
total.powerMah += powerMah;
}
private long calculateDuration(ControllerActivityCounter counter) {
if (counter == null) {
return 0;
}
return counter.getIdleTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
+ counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
+ counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
}
/** Returns bluetooth power usage based on the best data available. */
private double calculatePowerMah(@BatteryConsumer.PowerModel int powerModel,
long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower) {
if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
return uCtoMah(measuredChargeUC);
}
if (counter == null) {
return 0;
}
if (!ignoreReportedPower) {
final double powerMah =
counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
/ (double) (1000 * 60 * 60);
if (powerMah != 0) {
return powerMah;
}
}
if (!mHasBluetoothPowerController) {
return 0;
}
final long idleTimeMs =
counter.getIdleTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
final long rxTimeMs =
counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
final long txTimeMs =
counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
return calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
}
/** Returns estimated bluetooth power usage based on usage times. */
public double calculatePowerMah(long rxTimeMs, long txTimeMs, long idleTimeMs) {
return ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa))
/ (1000 * 60 * 60);
}
}