Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4cd04a25 authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Attribution of Bluetooth power per process state

Bug: 191921016
Test: atest FrameworksCoreTests:BatteryStatsTests
Change-Id: Ibcda229824a2966bd6adf8db7b9168050d77fe96
parent 2f473a97
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -192,6 +192,7 @@ public abstract class BatteryConsumer {
            POWER_COMPONENT_CPU,
            POWER_COMPONENT_MOBILE_RADIO,
            POWER_COMPONENT_WIFI,
            POWER_COMPONENT_BLUETOOTH,
    };

    static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
+10 −0
Original line number Diff line number Diff line
@@ -1036,6 +1036,16 @@ public abstract class BatteryStats implements Parcelable {
         */
        public abstract long getBluetoothMeasuredBatteryConsumptionUC();

        /**
         * Returns the battery consumption (in microcoulombs) of the uid's bluetooth usage
         * when in the specified process state.
         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
         *
         * {@hide}
         */
        public abstract long getBluetoothMeasuredBatteryConsumptionUC(
                @BatteryConsumer.ProcessState int processState);

        /**
         * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from
         * on device power measurement data.
+22 −1
Original line number Diff line number Diff line
@@ -265,6 +265,7 @@ public class BatteryStatsImpl extends BatteryStats {
            MeasuredEnergyStats.POWER_BUCKET_CPU,
            MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
            MeasuredEnergyStats.POWER_BUCKET_WIFI,
            MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
    };
    // TimeInState counters need NUM_PROCESS_STATE states in order to accommodate
@@ -8371,6 +8372,11 @@ public class BatteryStatsImpl extends BatteryStats {
            if (wifiControllerActivity != null) {
                wifiControllerActivity.setState(batteryConsumerProcessState, elapsedTimeMs);
            }
            final ControllerActivityCounterImpl bluetoothControllerActivity =
                    getBluetoothControllerActivity();
            if (bluetoothControllerActivity != null) {
                bluetoothControllerActivity.setState(batteryConsumerProcessState, elapsedTimeMs);
            }
            final MeasuredEnergyStats energyStats =
                    getOrCreateMeasuredEnergyStatsIfSupportedLocked();
            if (energyStats != null) {
@@ -8718,7 +8724,7 @@ public class BatteryStatsImpl extends BatteryStats {
        }
        @Override
        public ControllerActivityCounter getBluetoothControllerActivity() {
        public ControllerActivityCounterImpl getBluetoothControllerActivity() {
            return mBluetoothControllerActivity;
        }
@@ -8837,6 +8843,14 @@ public class BatteryStatsImpl extends BatteryStats {
            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH);
        }
        @GuardedBy("mBsi")
        @Override
        public long getBluetoothMeasuredBatteryConsumptionUC(
                @BatteryConsumer.ProcessState int processState) {
            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
                    processState);
        }
        @GuardedBy("mBsi")
        @Override
        public long getCpuMeasuredBatteryConsumptionUC() {
@@ -11424,6 +11438,13 @@ public class BatteryStatsImpl extends BatteryStats {
                    wifiControllerActivity.setState(batteryConsumerProcessState, elapsedRealtimeMs);
                }
                final ControllerActivityCounterImpl bluetoothControllerActivity =
                        getBluetoothControllerActivity();
                if (bluetoothControllerActivity != null) {
                    bluetoothControllerActivity.setState(batteryConsumerProcessState,
                            elapsedRealtimeMs);
                }
                final MeasuredEnergyStats energyStats =
                        getOrCreateMeasuredEnergyStatsIfSupportedLocked();
                if (energyStats != null) {
+152 −70
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.internal.os;

import android.annotation.Nullable;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryStats.ControllerActivityCounter;
@@ -26,19 +27,33 @@ import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;

import java.util.Arrays;
import java.util.List;

public class BluetoothPowerCalculator extends PowerCalculator {
    private static final String TAG = "BluetoothPowerCalc";
    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;

    private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];

    private final double mIdleMa;
    private final double mRxMa;
    private final double mTxMa;
    private final boolean mHasBluetoothPowerController;

    private static class PowerAndDuration {
        // Return value of BT duration per app
        public long durationMs;
        // Return value of BT power per app
        public double powerMah;

        public BatteryConsumer.Key[] keys;
        public double[] powerPerKeyMah;

        // Aggregated BT duration across all apps
        public long totalDurationMs;
        // Aggregated BT power across all apps
        public double totalPowerMah;
    }

    public BluetoothPowerCalculator(PowerProfile profile) {
@@ -55,59 +70,88 @@ public class BluetoothPowerCalculator extends PowerCalculator {
            return;
        }

        final PowerAndDuration total = new PowerAndDuration();
        BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
        final PowerAndDuration powerAndDuration = 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);
            if (keys == UNINITIALIZED_KEYS) {
                if (query.isProcessStateDataNeeded()) {
                    keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_BLUETOOTH);
                    powerAndDuration.keys = keys;
                    powerAndDuration.powerPerKeyMah = new double[keys.length];
                } else {
                    keys = null;
                }
            }
            calculateApp(app, powerAndDuration, 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());
        calculatePowerAndDuration(null, powerModel, measuredChargeUC,
                activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration);

        // Subtract what the apps used, but clamp to 0.
        final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs);
        final long systemComponentDurationMs = Math.max(0,
                powerAndDuration.durationMs - powerAndDuration.totalDurationMs);
        if (DEBUG) {
            Log.d(TAG, "Bluetooth active: time=" + (systemComponentDurationMs)
                    + " power=" + formatCharge(systemPowerMah));
                    + " power=" + formatCharge(powerAndDuration.powerMah));
        }

        builder.getAggregateBatteryConsumerBuilder(
                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
                .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, systemDurationMs)
                .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
                        powerAndDuration.durationMs)
                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
                        Math.max(systemPowerMah, total.powerMah), powerModel);
                        Math.max(powerAndDuration.powerMah, powerAndDuration.totalPowerMah),
                        powerModel);

        builder.getAggregateBatteryConsumerBuilder(
                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
                .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.durationMs)
                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.powerMah,
                .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
                        powerAndDuration.totalDurationMs)
                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
                        powerAndDuration.totalPowerMah,
                        powerModel);
    }

    private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total,
    private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration powerAndDuration,
            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());
        calculatePowerAndDuration(app.getBatteryStatsUid(), powerModel, measuredChargeUC,
                activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration);

        app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, durationMs)
                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah, powerModel);
        app.setUsageDurationMillis(
                        BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerAndDuration.durationMs)
                .setConsumedPower(
                        BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerAndDuration.powerMah,
                        powerModel);

        powerAndDuration.totalDurationMs += powerAndDuration.durationMs;
        powerAndDuration.totalPowerMah += powerAndDuration.powerMah;

        if (query.isProcessStateDataNeeded() && powerAndDuration.keys != null) {
            for (int j = 0; j < powerAndDuration.keys.length; j++) {
                BatteryConsumer.Key key = powerAndDuration.keys[j];
                final int processState = key.processState;
                if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
                    // Already populated with the powerAndDuration across all process states
                    continue;
                }

        total.durationMs += durationMs;
        total.powerMah += powerMah;
                app.setConsumedPower(key, powerAndDuration.powerPerKeyMah[j], powerModel);
            }
        }
    }

    @Override
@@ -117,12 +161,12 @@ public class BluetoothPowerCalculator extends PowerCalculator {
            return;
        }

        PowerAndDuration total = new PowerAndDuration();
        PowerAndDuration powerAndDuration = 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);
                calculateApp(app, app.uidObj, statsType, powerAndDuration);
            }
        }

@@ -131,13 +175,14 @@ public class BluetoothPowerCalculator extends PowerCalculator {
        final int powerModel = getPowerModel(measuredChargeUC);
        final ControllerActivityCounter activityCounter =
                batteryStats.getBluetoothControllerActivity();
        final long systemDurationMs = calculateDuration(activityCounter);
        final double systemPowerMah =
                calculatePowerMah(powerModel, measuredChargeUC, activityCounter, false);
        calculatePowerAndDuration(null, powerModel, measuredChargeUC, activityCounter, false,
                powerAndDuration);

        // 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);
        final double powerMah = Math.max(0,
                powerAndDuration.powerMah - powerAndDuration.totalPowerMah);
        final long durationMs = Math.max(0,
                powerAndDuration.durationMs - powerAndDuration.totalDurationMs);
        if (DEBUG && powerMah != 0) {
            Log.d(TAG, "Bluetooth active: time=" + (durationMs)
                    + " power=" + formatCharge(powerMah));
@@ -160,65 +205,102 @@ public class BluetoothPowerCalculator extends PowerCalculator {
    }

    private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
            PowerAndDuration total) {

            PowerAndDuration powerAndDuration) {
        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);
        calculatePowerAndDuration(u, powerModel, measuredChargeUC, activityCounter,
                false, powerAndDuration);

        app.bluetoothRunningTimeMs = durationMs;
        app.bluetoothPowerMah = powerMah;
        app.bluetoothRunningTimeMs = powerAndDuration.durationMs;
        app.bluetoothPowerMah = powerAndDuration.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;
        powerAndDuration.totalDurationMs += powerAndDuration.durationMs;
        powerAndDuration.totalPowerMah += powerAndDuration.powerMah;
    }

    private long calculateDuration(ControllerActivityCounter counter) {
    /** Returns bluetooth power usage based on the best data available. */
    private void calculatePowerAndDuration(@Nullable BatteryStats.Uid uid,
            @BatteryConsumer.PowerModel int powerModel,
            long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower,
            PowerAndDuration powerAndDuration) {
        if (counter == null) {
            return 0;
            powerAndDuration.durationMs = 0;
            powerAndDuration.powerMah = 0;
            if (powerAndDuration.powerPerKeyMah != null) {
                Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
            }

        return counter.getIdleTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
                + counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
                + counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
            return;
        }

    /** Returns bluetooth power usage based on the best data available. */
    private double calculatePowerMah(@BatteryConsumer.PowerModel int powerModel,
            long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower) {
        final BatteryStats.LongCounter idleTimeCounter = counter.getIdleTimeCounter();
        final BatteryStats.LongCounter rxTimeCounter = counter.getRxTimeCounter();
        final BatteryStats.LongCounter txTimeCounter = counter.getTxTimeCounters()[0];
        final long idleTimeMs = idleTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
        final long rxTimeMs = rxTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
        final long txTimeMs = txTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);

        powerAndDuration.durationMs = idleTimeMs + rxTimeMs + txTimeMs;

        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
            return uCtoMah(measuredChargeUC);
            powerAndDuration.powerMah = uCtoMah(measuredChargeUC);
            if (uid != null && powerAndDuration.keys != null) {
                for (int i = 0; i < powerAndDuration.keys.length; i++) {
                    BatteryConsumer.Key key = powerAndDuration.keys[i];
                    final int processState = key.processState;
                    if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
                        // Already populated with the powerAndDuration across all process states
                        continue;
                    }

        if (counter == null) {
            return 0;
                    powerAndDuration.powerPerKeyMah[i] =
                            uCtoMah(uid.getBluetoothMeasuredBatteryConsumptionUC(processState));
                }

            }
        } else {
            if (!ignoreReportedPower) {
                final double powerMah =
                        counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
                                / (double) (1000 * 60 * 60);
                if (powerMah != 0) {
                return powerMah;
                    powerAndDuration.powerMah = powerMah;
                    if (powerAndDuration.powerPerKeyMah != null) {
                        // Leave this use case unsupported: used energy is reported
                        // via BluetoothActivityEnergyInfo rather than PowerStats HAL.
                        Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
                    }
                    return;
                }
            }

        if (!mHasBluetoothPowerController) {
            return 0;
            if (mHasBluetoothPowerController) {
                powerAndDuration.powerMah = calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);

                if (powerAndDuration.keys != null) {
                    for (int i = 0; i < powerAndDuration.keys.length; i++) {
                        BatteryConsumer.Key key = powerAndDuration.keys[i];
                        final int processState = key.processState;
                        if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
                            // Already populated with the powerAndDuration across all process states
                            continue;
                        }

        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);
                        powerAndDuration.powerPerKeyMah[i] =
                                calculatePowerMah(
                                        rxTimeCounter.getCountForProcessState(processState),
                                        txTimeCounter.getCountForProcessState(processState),
                                        idleTimeCounter.getCountForProcessState(processState));
                    }
                }
            } else {
                powerAndDuration.powerMah = 0;
                if (powerAndDuration.powerPerKeyMah != null) {
                    Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
                }
            }
        }
    }

    /** Returns estimated bluetooth power usage based on usage times. */
+1 −1
Original line number Diff line number Diff line
@@ -83,7 +83,7 @@ public class BatteryUsageStatsTest {
        final Parcel parcel = Parcel.obtain();
        parcel.writeParcelable(outBatteryUsageStats, 0);

        assertThat(parcel.dataSize()).isLessThan(6000);
        assertThat(parcel.dataSize()).isLessThan(7000);

        parcel.setDataPosition(0);

Loading