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

Commit 4127267e authored by Adam Bookatz's avatar Adam Bookatz
Browse files

BatteryStats measured energy for Wifi & Bluetooth

Measured energy data for Wifi is received, processed, attributed
to uids, and delivered. Same for Bluetooth.

Since Wifi power analysis is done via two methods (ControllerActivity data
or packet data), depending on what is available on the device, the
measured energy method also takes into account these two methods for
attribution purposes.

For Bluetooth, the only raw data is ControllerActivity data, so it is
simpler.

In both cases, if that data already contains energy data from the
ControllerActivity, but the device also
has measured energy data, then the controller energy data is used for
normalization but is otherwise replaced by the measured energy data. In
other words, the measured energy data is treated preferentially.

Also fixes a minor bug in BSI.initMeasuredEnergyStatsLocked() which
had a stray early return line.

Test: atest BatteryStatsTests
Bug: 174818228
Change-Id: Id4728fcd5e6f226664e578a4c19c4aaa5246ff4b
parent cdb0369d
Loading
Loading
Loading
Loading
+43 −7
Original line number Diff line number Diff line
@@ -986,13 +986,13 @@ public abstract class BatteryStats implements Parcelable {
        public abstract void getDeferredJobsLineLocked(StringBuilder sb, int which);

        /**
         * Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
         * Returns the battery consumption (in microcoulombs) of bluetooth for this uid,
         * derived from on device power measurement data.
         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
         *
         * {@hide}
         */
        public abstract long getScreenOnMeasuredBatteryConsumptionUC();
        public abstract long getBluetoothMeasuredBatteryConsumptionUC();

        /**
         * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from
@@ -1003,6 +1003,24 @@ public abstract class BatteryStats implements Parcelable {
         */
        public abstract long getCpuMeasuredBatteryConsumptionUC();

        /**
         * Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
         * derived from on device power measurement data.
         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
         *
         * {@hide}
         */
        public abstract long getScreenOnMeasuredBatteryConsumptionUC();

        /**
         * Returns the battery consumption (in microcoulombs) of wifi for this uid,
         * derived from on device power measurement data.
         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
         *
         * {@hide}
         */
        public abstract long getWifiMeasuredBatteryConsumptionUC();

        /**
         * Returns the battery consumption (in microcoulombs) used by this uid for each
         * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
@@ -2505,11 +2523,29 @@ public abstract class BatteryStats implements Parcelable {
    };

    /**
     * Returned value if power data is unavailable
     * Returned value if power data is unavailable.
     *
     * {@hide}
     */
    public static final long POWER_DATA_UNAVAILABLE = -1L;

    /**
     * Returns the battery consumption (in microcoulombs) of bluetooth, derived from on
     * device power measurement data.
     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
     *
     * {@hide}
     */
    public static final long POWER_DATA_UNAVAILABLE = -1;
    public abstract long getBluetoothMeasuredBatteryConsumptionUC();

    /**
     * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power
     * measurement data.
     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
     *
     * {@hide}
     */
    public abstract long getCpuMeasuredBatteryConsumptionUC();

    /**
     * Returns the battery consumption (in microcoulombs) of the screen while on, derived from on
@@ -2530,13 +2566,13 @@ public abstract class BatteryStats implements Parcelable {
    public abstract long getScreenDozeMeasuredBatteryConsumptionUC();

    /**
     * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power
     * measurement data.
     * Returns the battery consumption (in microcoulombs) of wifi, derived from on
     * device power measurement data.
     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
     *
     * {@hide}
     */
    public abstract long getCpuMeasuredBatteryConsumptionUC();
    public abstract long getWifiMeasuredBatteryConsumptionUC();

    /**
     * Returns the battery consumption (in microcoulombs) that each
+2 −2
Original line number Diff line number Diff line
@@ -110,7 +110,7 @@ public final class WifiActivityEnergyInfo implements Parcelable {
        }
        // Calculate energy used using PowerProfile.
        PowerProfile powerProfile = new PowerProfile(context);
        final double rxIdleCurrent = powerProfile.getAveragePower(
        final double idleCurrent = powerProfile.getAveragePower(
                PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
        final double rxCurrent = powerProfile.getAveragePower(
                PowerProfile.POWER_WIFI_CONTROLLER_RX);
@@ -121,7 +121,7 @@ public final class WifiActivityEnergyInfo implements Parcelable {

        return (long) ((txDurationMillis * txCurrent
                + rxDurationMillis * rxCurrent
                + idleDurationMillis * rxIdleCurrent)
                + idleDurationMillis * idleCurrent)
                * voltage);
    }

+226 −52

File changed.

Preview size limit exceeded, changes collapsed.

+34 −13
Original line number Diff line number Diff line
@@ -15,8 +15,11 @@
 */
package com.android.internal.os;

import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;

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;
@@ -65,17 +68,19 @@ public class BluetoothPowerCalculator extends PowerCalculator {
                builder.getUidBatteryConsumerBuilders();
        for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
            final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
            calculateApp(app, total);
            calculateApp(app, total, query);
            if (app.getUid() == Process.BLUETOOTH_UID) {
                app.excludeFromBatteryUsageStats();
                systemBatteryConsumerBuilder.addUidBatteryConsumer(app);
            }
        }

        final BatteryStats.ControllerActivityCounter activityCounter =
        final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ?
                POWER_DATA_UNAVAILABLE : batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
        final ControllerActivityCounter activityCounter =
                batteryStats.getBluetoothControllerActivity();
        final long systemDurationMs = calculateDuration(activityCounter);
        final double systemPowerMah = calculatePower(activityCounter);
        final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter);

        // Subtract what the apps used, but clamp to 0.
        final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs);
@@ -91,11 +96,16 @@ public class BluetoothPowerCalculator extends PowerCalculator {
                        systemComponentPowerMah);
    }

    private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total) {
        final BatteryStats.ControllerActivityCounter activityCounter =
    private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total,
            BatteryUsageStatsQuery query) {

        final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ?
                POWER_DATA_UNAVAILABLE :
                app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC();
        final ControllerActivityCounter activityCounter =
                app.getBatteryStatsUid().getBluetoothControllerActivity();
        final long durationMs = calculateDuration(activityCounter);
        final double powerMah = calculatePower(activityCounter);
        final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter);

        app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, durationMs)
                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah);
@@ -121,10 +131,11 @@ public class BluetoothPowerCalculator extends PowerCalculator {
        }

        BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
        final BatteryStats.ControllerActivityCounter activityCounter =
        final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
        final ControllerActivityCounter activityCounter =
                batteryStats.getBluetoothControllerActivity();
        final double systemPowerMah = calculatePower(activityCounter);
        final long systemDurationMs = calculateDuration(activityCounter);
        final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter);

        // Subtract what the apps used, but clamp to 0.
        final double powerMah = Math.max(0, systemPowerMah - total.powerMah);
@@ -152,10 +163,11 @@ public class BluetoothPowerCalculator extends PowerCalculator {

    private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
            PowerAndDuration total) {
        final BatteryStats.ControllerActivityCounter activityCounter =
                u.getBluetoothControllerActivity();

        final long measuredChargeUC = u.getBluetoothMeasuredBatteryConsumptionUC();
        final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity();
        final long durationMs = calculateDuration(activityCounter);
        final double powerMah = calculatePower(activityCounter);
        final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter);

        app.bluetoothRunningTimeMs = durationMs;
        app.bluetoothPowerMah = powerMah;
@@ -166,7 +178,7 @@ public class BluetoothPowerCalculator extends PowerCalculator {
        total.powerMah += powerMah;
    }

    private long calculateDuration(BatteryStats.ControllerActivityCounter counter) {
    private long calculateDuration(ControllerActivityCounter counter) {
        if (counter == null) {
            return 0;
        }
@@ -176,7 +188,11 @@ public class BluetoothPowerCalculator extends PowerCalculator {
                + counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
    }

    private double calculatePower(BatteryStats.ControllerActivityCounter counter) {
    /** Returns bluetooth power usage based on the best data available. */
    private double calculatePowerMah(long measuredChargeUC, ControllerActivityCounter counter) {
        if (measuredChargeUC != POWER_DATA_UNAVAILABLE) {
            return uCtoMah(measuredChargeUC);
        }
        if (counter == null) {
            return 0;
        }
@@ -195,6 +211,11 @@ public class BluetoothPowerCalculator extends PowerCalculator {
                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);
    }
+83 −46
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 */
package com.android.internal.os;

import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;

import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
@@ -79,11 +81,6 @@ public class WifiPowerCalculator extends PowerCalculator {
    public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {

        // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point,
        // so always check this field.
        final boolean hasWifiPowerReporting =
                mHasWifiPowerController && batteryStats.hasWifiActivityReporting();

        final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
                builder.getOrCreateSystemBatteryConsumerBuilder(
                        SystemBatteryConsumer.DRAIN_TYPE_WIFI);
@@ -97,7 +94,8 @@ public class WifiPowerCalculator extends PowerCalculator {
            final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
            calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), rawRealtimeUs,
                    BatteryStats.STATS_SINCE_CHARGED,
                    hasWifiPowerReporting);
                    batteryStats.hasWifiActivityReporting(),
                    query.shouldForceUsePowerProfileModel());

            totalAppDurationMs += powerDurationAndTraffic.durationMs;
            totalAppPowerMah += powerDurationAndTraffic.powerMah;
@@ -115,7 +113,9 @@ public class WifiPowerCalculator extends PowerCalculator {

        calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs,
                BatteryStats.STATS_SINCE_CHARGED,
                hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah);
                batteryStats.hasWifiActivityReporting(),
                query.shouldForceUsePowerProfileModel(),
                totalAppDurationMs, totalAppPowerMah);

        systemBatteryConsumerBuilder
                .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI,
@@ -135,11 +135,6 @@ public class WifiPowerCalculator extends PowerCalculator {
    public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
            long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {

        // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point,
        // so always check this field.
        final boolean hasWifiPowerReporting =
                mHasWifiPowerController && batteryStats.hasWifiActivityReporting();

        final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0);

        long totalAppDurationMs = 0;
@@ -149,7 +144,7 @@ public class WifiPowerCalculator extends PowerCalculator {
            final BatterySipper app = sippers.get(i);
            if (app.drainType == BatterySipper.DrainType.APP) {
                calculateApp(powerDurationAndTraffic, app.uidObj, rawRealtimeUs, statsType,
                        hasWifiPowerReporting);
                        batteryStats.hasWifiActivityReporting(), /* force use power model*/ false);

                totalAppDurationMs += powerDurationAndTraffic.durationMs;
                totalAppPowerMah += powerDurationAndTraffic.powerMah;
@@ -169,7 +164,8 @@ public class WifiPowerCalculator extends PowerCalculator {
        }

        calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, statsType,
                hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah);
                batteryStats.hasWifiActivityReporting(), /* force use power model*/ false,
                totalAppDurationMs, totalAppPowerMah);

        bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs;
        bs.wifiPowerMah += powerDurationAndTraffic.powerMah;
@@ -180,8 +176,9 @@ public class WifiPowerCalculator extends PowerCalculator {
    }

    private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u,
            long rawRealtimeUs,
            int statsType, boolean hasWifiPowerReporting) {
            long rawRealtimeUs, int statsType,
            boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel) {

        powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets(
                BatteryStats.NETWORK_WIFI_RX_DATA,
                statsType);
@@ -195,7 +192,14 @@ public class WifiPowerCalculator extends PowerCalculator {
                BatteryStats.NETWORK_WIFI_TX_DATA,
                statsType);

        if (hasWifiPowerReporting) {
        final long measuredChargeUC = u.getWifiMeasuredBatteryConsumptionUC();
        final boolean isMeasuredPowerAvailable
                = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE;
        if (isMeasuredPowerAvailable) {
            powerDurationAndTraffic.powerMah = uCtoMah(measuredChargeUC);
        }

        if (hasWifiActivityReporting && mHasWifiPowerController) {
            final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
            if (counter != null) {
                final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
@@ -203,9 +207,10 @@ public class WifiPowerCalculator extends PowerCalculator {
                final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);

                powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime;
                powerDurationAndTraffic.powerMah = mIdlePowerEstimator.calculatePower(idleTime)
                        + mTxPowerEstimator.calculatePower(txTime)
                        + mRxPowerEstimator.calculatePower(rxTime);
                if (!isMeasuredPowerAvailable) {
                    powerDurationAndTraffic.powerMah
                            = calcPowerFromControllerDataMah(rxTime, txTime, idleTime);
                }

                if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
                    Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime
@@ -214,21 +219,20 @@ public class WifiPowerCalculator extends PowerCalculator {
                }
            }
        } else {
            final double wifiPacketPower = (
                    powerDurationAndTraffic.wifiRxPackets + powerDurationAndTraffic.wifiTxPackets)
                    * mWifiPowerPerPacket;
            final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
            powerDurationAndTraffic.durationMs = wifiRunningTime;

            if (!isMeasuredPowerAvailable) {
                final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
            long batchScanTimeMs = 0;
                long batchTimeMs = 0;
                for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
                batchScanTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
                    batchTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
                }
                powerDurationAndTraffic.powerMah = calcPowerWithoutControllerDataMah(
                        powerDurationAndTraffic.wifiRxPackets,
                        powerDurationAndTraffic.wifiTxPackets,
                        wifiRunningTime, wifiScanTimeMs, batchTimeMs);
            }

            powerDurationAndTraffic.durationMs = wifiRunningTime;
            powerDurationAndTraffic.powerMah = wifiPacketPower
                    + mPowerOnPowerEstimator.calculatePower(wifiRunningTime)
                    + mScanPowerEstimator.calculatePower(wifiScanTimeMs)
                    + mBatchScanPowerEstimator.calculatePower(batchScanTimeMs);

            if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
                Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(
@@ -238,12 +242,20 @@ public class WifiPowerCalculator extends PowerCalculator {
    }

    private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic,
            BatteryStats stats, long rawRealtimeUs,
            int statsType, boolean hasWifiPowerReporting, long totalAppDurationMs,
            double totalAppPowerMah) {
            BatteryStats stats, long rawRealtimeUs, int statsType,
            boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel,
            long totalAppDurationMs, double totalAppPowerMah) {

        long totalDurationMs;
        double totalPowerMah;
        if (hasWifiPowerReporting) {
        double totalPowerMah = 0;

        final long measuredChargeUC = stats.getWifiMeasuredBatteryConsumptionUC();
        final boolean isMeasuredPowerAvailable
                = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE;
        if (isMeasuredPowerAvailable) {
            totalPowerMah = uCtoMah(measuredChargeUC);
        }
        if (hasWifiActivityReporting && mHasWifiPowerController) {
            final BatteryStats.ControllerActivityCounter counter =
                    stats.getWifiControllerActivity();

@@ -253,17 +265,19 @@ public class WifiPowerCalculator extends PowerCalculator {

            totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs;

            totalPowerMah =
                    counter.getPowerCounter().getCountLocked(statsType) / (double) (1000 * 60 * 60);
            if (!isMeasuredPowerAvailable) {
                totalPowerMah = counter.getPowerCounter().getCountLocked(statsType)
                        / (double) (1000 * 60 * 60);
                if (totalPowerMah == 0) {
                    // Some controllers do not report power drain, so we can calculate it here.
                totalPowerMah = mIdlePowerEstimator.calculatePower(idleTimeMs)
                        + mTxPowerEstimator.calculatePower(txTimeMs)
                        + mRxPowerEstimator.calculatePower(rxTimeMs);
                    totalPowerMah = calcPowerFromControllerDataMah(rxTimeMs, txTimeMs, idleTimeMs);
                }
            }
        } else {
            totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000;
            totalPowerMah = mPowerOnPowerEstimator.calculatePower(totalDurationMs);
            if (!isMeasuredPowerAvailable) {
                totalPowerMah = calcGlobalPowerWithoutControllerDataMah(totalDurationMs);
            }
        }

        powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs);
@@ -274,6 +288,29 @@ public class WifiPowerCalculator extends PowerCalculator {
        }
    }

    /** Returns (global or uid) estimated wifi power used using WifiControllerActivity data. */
    public double calcPowerFromControllerDataMah(long rxTimeMs, long txTimeMs, long idleTimeMs) {
        return mRxPowerEstimator.calculatePower(rxTimeMs)
                + mTxPowerEstimator.calculatePower(txTimeMs)
                + mIdlePowerEstimator.calculatePower(idleTimeMs);
    }

    /** Returns per-uid estimated wifi power used using non-WifiControllerActivity data. */
    public double calcPowerWithoutControllerDataMah(long rxPackets, long txPackets,
            long wifiRunningTimeMs, long wifiScanTimeMs, long wifiBatchScanTimeMs) {
        return
                (rxPackets + txPackets) * mWifiPowerPerPacket
                + mPowerOnPowerEstimator.calculatePower(wifiRunningTimeMs)
                + mScanPowerEstimator.calculatePower(wifiScanTimeMs)
                + mBatchScanPowerEstimator.calculatePower(wifiBatchScanTimeMs);

    }

    /** Returns global estimated wifi power used using non-WifiControllerActivity data. */
    public double calcGlobalPowerWithoutControllerDataMah(long globalWifiRunningTimeMs) {
        return mPowerOnPowerEstimator.calculatePower(globalWifiRunningTimeMs);
    }

    /**
     * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB.
     */
Loading