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

Commit f4f95283 authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Update CpuPowerCalculator to do per process state attribution

Bug: 191921016
Test: atest FrameworksCoreTests:BatteryStatsTests

Change-Id: I6c69f2b2aa03a61ade6c087e17891c079bc3e054
parent d7be67b7
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -756,6 +756,11 @@ public abstract class BatteryConsumer {
            mPowerComponentsBuilder = new PowerComponents.Builder(data);
        }

        @Nullable
        public Key[] getKeys(@PowerComponent int componentId) {
            return mData.getKeys(componentId);
        }

        @Nullable
        public Key getKey(@PowerComponent int componentId, @ProcessState int processState) {
            return mData.getKey(componentId, processState);
+11 −0
Original line number Diff line number Diff line
@@ -660,6 +660,7 @@ public abstract class BatteryStats implements Parcelable {
            mapUidProcessStateToBatteryConsumerProcessState(int processState) {
        switch (processState) {
            case BatteryStats.Uid.PROCESS_STATE_TOP:
            case BatteryStats.Uid.PROCESS_STATE_FOREGROUND:
                return BatteryConsumer.PROCESS_STATE_FOREGROUND;
            case BatteryStats.Uid.PROCESS_STATE_BACKGROUND:
            case BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING:
@@ -1027,6 +1028,16 @@ public abstract class BatteryStats implements Parcelable {
         */
        public abstract long getCpuMeasuredBatteryConsumptionUC();

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

        /**
         * Returns the battery consumption (in microcoulombs) of the uid's GNSS usage, derived from
         * on device power measurement data.
+4 −0
Original line number Diff line number Diff line
@@ -111,6 +111,10 @@ public final class BatteryUsageStatsQuery implements Parcelable {
        return (mFlags & FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL) != 0;
    }

    public boolean isProcessStateDataNeeded() {
        return (mFlags & FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0;
    }

    /**
     * Returns the client's tolerance for stale battery stats. The data is allowed to be up to
     * this many milliseconds out-of-date.
+28 −0
Original line number Diff line number Diff line
@@ -588,6 +588,10 @@ public class BatteryStatsImpl extends BatteryStats {
                    procState = u.mProcessState;
                }
                if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
                    continue;
                }
                final long timestampMs = mClock.elapsedRealtime();
                final LongArrayMultiStateCounter onBatteryCounter =
                        u.getProcStateTimeCounter().getCounter();
@@ -8599,6 +8603,23 @@ public class BatteryStatsImpl extends BatteryStats {
            return mUidMeasuredEnergyStats.getAccumulatedStandardBucketCharge(bucket);
        }
        /**
         * Returns the battery consumption (in microcoulombs) of this uid for a standard power
         * bucket and a process state, such as Uid.PROCESS_STATE_TOP.
         */
        @GuardedBy("mBsi")
        public long getMeasuredBatteryConsumptionUC(@StandardPowerBucket int bucket,
                int processState) {
            if (mBsi.mGlobalMeasuredEnergyStats == null
                    || !mBsi.mGlobalMeasuredEnergyStats.isStandardBucketSupported(bucket)) {
                return POWER_DATA_UNAVAILABLE;
            }
            if (mUidMeasuredEnergyStats == null) {
                return 0L; // It is supported, but was never filled, so it must be 0
            }
            return mUidMeasuredEnergyStats.getAccumulatedStandardBucketCharge(bucket, processState);
        }
        @GuardedBy("mBsi")
        @Override
        public long[] getCustomConsumerMeasuredBatteryConsumptionUC() {
@@ -8625,6 +8646,13 @@ public class BatteryStatsImpl extends BatteryStats {
        }
        @GuardedBy("mBsi")
        @Override
        public long getCpuMeasuredBatteryConsumptionUC(
                @BatteryConsumer.ProcessState int processState) {
            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU,
                    processState);
        }
        @Override
        public long getGnssMeasuredBatteryConsumptionUC() {
            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
+121 −29
Original line number Diff line number Diff line
@@ -25,11 +25,13 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;

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

public class CpuPowerCalculator extends PowerCalculator {
    private static final String TAG = "CpuPowerCalculator";
    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
    private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
    private final int mNumCpuClusters;

    // Time-in-state based CPU power estimation model computes the estimated power
@@ -44,13 +46,16 @@ public class CpuPowerCalculator extends PowerCalculator {
    private final UsageBasedPowerEstimator[] mPerClusterPowerEstimators;
    // Multiple estimators per cluster: one per available scaling frequency. Note that different
    // clusters have different sets of frequencies and corresponding power consumption averages.
    private final UsageBasedPowerEstimator[][] mPerCpuFreqPowerEstimators;
    private final UsageBasedPowerEstimator[][] mPerCpuFreqPowerEstimatorsByCluster;
    // Flattened array of estimators across clusters
    private final UsageBasedPowerEstimator[] mPerCpuFreqPowerEstimators;

    private static class Result {
        public long durationMs;
        public double powerMah;
        public long durationFgMs;
        public String packageWithHighestDrain;
        public double[] perProcStatePowerMah;
    }

    public CpuPowerCalculator(PowerProfile profile) {
@@ -65,14 +70,23 @@ public class CpuPowerCalculator extends PowerCalculator {
                    profile.getAveragePowerForCpuCluster(cluster));
        }

        mPerCpuFreqPowerEstimators = new UsageBasedPowerEstimator[mNumCpuClusters][];
        int freqCount = 0;
        for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
            freqCount += profile.getNumSpeedStepsInCpuCluster(cluster);
        }

        mPerCpuFreqPowerEstimatorsByCluster = new UsageBasedPowerEstimator[mNumCpuClusters][];
        mPerCpuFreqPowerEstimators = new UsageBasedPowerEstimator[freqCount];
        int index = 0;
        for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
            final int speedsForCluster = profile.getNumSpeedStepsInCpuCluster(cluster);
            mPerCpuFreqPowerEstimators[cluster] = new UsageBasedPowerEstimator[speedsForCluster];
            mPerCpuFreqPowerEstimatorsByCluster[cluster] =
                    new UsageBasedPowerEstimator[speedsForCluster];
            for (int speed = 0; speed < speedsForCluster; speed++) {
                mPerCpuFreqPowerEstimators[cluster][speed] =
                        new UsageBasedPowerEstimator(
                final UsageBasedPowerEstimator estimator = new UsageBasedPowerEstimator(
                        profile.getAveragePowerForCpuCore(cluster, speed));
                mPerCpuFreqPowerEstimatorsByCluster[cluster][speed] = estimator;
                mPerCpuFreqPowerEstimators[index++] = estimator;
            }
        }
    }
@@ -82,12 +96,20 @@ public class CpuPowerCalculator extends PowerCalculator {
            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
        double totalPowerMah = 0;

        BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
        Result result = new Result();
        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, app.getBatteryStatsUid(), query, result);
            if (keys == UNINITIALIZED_KEYS) {
                if (query.isProcessStateDataNeeded()) {
                    keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_CPU);
                } else {
                    keys = null;
                }
            }
            calculateApp(app, app.getBatteryStatsUid(), query, result, keys);
            totalPowerMah += result.powerMah;
        }

@@ -105,7 +127,7 @@ public class CpuPowerCalculator extends PowerCalculator {
    }

    private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
            BatteryUsageStatsQuery query, Result result) {
            BatteryUsageStatsQuery query, Result result, BatteryConsumer.Key[] keys) {
        final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC();
        final int powerModel = getPowerModel(consumptionUC, query);
        calculatePowerAndDuration(u, powerModel, consumptionUC, BatteryStats.STATS_SINCE_CHARGED,
@@ -114,6 +136,75 @@ public class CpuPowerCalculator extends PowerCalculator {
        app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah, powerModel)
                .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, result.durationMs)
                .setPackageWithHighestDrain(result.packageWithHighestDrain);

        if (query.isProcessStateDataNeeded() && keys != null) {
            switch (powerModel) {
                case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
                    calculateMeasuredPowerPerProcessState(app, u, keys);
                    break;
                case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
                    calculateModeledPowerPerProcessState(app, u, keys, result);
                    break;
            }
        }
    }

    private void calculateMeasuredPowerPerProcessState(UidBatteryConsumer.Builder app,
            BatteryStats.Uid u, BatteryConsumer.Key[] keys) {
        for (BatteryConsumer.Key key : keys) {
            // The key for "PROCESS_STATE_ANY" has already been populated with the
            // full energy across all states.  We don't want to override it with
            // the energy for "other" states, which excludes the tracked states like
            // foreground, background etc.
            if (key.processState == BatteryConsumer.PROCESS_STATE_ANY) {
                continue;
            }

            final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC(key.processState);
            if (consumptionUC != 0) {
                app.setConsumedPower(key, uCtoMah(consumptionUC),
                        BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
            }
        }
    }

    private void calculateModeledPowerPerProcessState(UidBatteryConsumer.Builder app,
            BatteryStats.Uid u, BatteryConsumer.Key[] keys, Result result) {
        if (result.perProcStatePowerMah == null) {
            result.perProcStatePowerMah = new double[BatteryConsumer.PROCESS_STATE_COUNT];
        } else {
            Arrays.fill(result.perProcStatePowerMah, 0);
        }

        for (int uidProcState = 0; uidProcState < BatteryStats.Uid.NUM_PROCESS_STATE;
                uidProcState++) {
            @BatteryConsumer.ProcessState int procState =
                    BatteryStats.mapUidProcessStateToBatteryConsumerProcessState(uidProcState);
            if (procState == BatteryConsumer.PROCESS_STATE_ANY) {
                continue;
            }

            // TODO(b/191921016): use per-state CPU active time
            final long cpuActiveTime = 0;
            // TODO(b/191921016): use per-state CPU cluster times
            final long[] cpuClusterTimes = null;

            final long[] cpuFreqTimes = u.getCpuFreqTimes(BatteryStats.STATS_SINCE_CHARGED,
                    uidProcState);
            if (cpuActiveTime != 0 || cpuClusterTimes != null || cpuFreqTimes != null) {
                result.perProcStatePowerMah[procState] += calculateUidModeledPowerMah(u,
                        cpuActiveTime, cpuClusterTimes, cpuFreqTimes);
            }
        }

        for (BatteryConsumer.Key key : keys) {
            if (key.processState == BatteryConsumer.PROCESS_STATE_ANY) {
                continue;
            }

            app.setConsumedPower(key, result.perProcStatePowerMah[key.processState],
                    BatteryConsumer.POWER_MODEL_POWER_PROFILE);
        }
    }

    @Override
@@ -205,16 +296,21 @@ public class CpuPowerCalculator extends PowerCalculator {
     * Calculates CPU power consumed by the specified app, using the PowerProfile model.
     */
    public double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
        return calculateUidModeledPowerMah(u, u.getCpuActiveTime(), u.getCpuClusterTimes(),
                u.getCpuFreqTimes(statsType));
    }

    private double calculateUidModeledPowerMah(BatteryStats.Uid u, long cpuActiveTime,
            long[] cpuClusterTimes, long[] cpuFreqTimes) {
        // Constant battery drain when CPU is active
        double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());
        double powerMah = calculateActiveCpuPowerMah(cpuActiveTime);

        // Additional per-cluster battery drain
        long[] cpuClusterTimes = u.getCpuClusterTimes();
        if (cpuClusterTimes != null) {
            if (cpuClusterTimes.length == mNumCpuClusters) {
                for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
                    double power = calculatePerCpuClusterPowerMah(cluster,
                            cpuClusterTimes[cluster]);
                    final double power = mPerClusterPowerEstimators[cluster]
                            .calculatePower(cpuClusterTimes[cluster]);
                    powerMah += power;
                    if (DEBUG) {
                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
@@ -228,21 +324,17 @@ public class CpuPowerCalculator extends PowerCalculator {
            }
        }

        // Additional per-frequency battery drain
        for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
            final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length;
            for (int speed = 0; speed < speedsForCluster; speed++) {
                final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
                final double power = calculatePerCpuFreqPowerMah(cluster, speed,
                        timeUs / 1000);
                if (DEBUG) {
                    Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
                            + speed + " timeUs=" + timeUs + " power="
                            + formatCharge(power));
        if (cpuFreqTimes != null) {
            if (cpuFreqTimes.length == mPerCpuFreqPowerEstimators.length) {
                for (int i = 0; i < cpuFreqTimes.length; i++) {
                    powerMah += mPerCpuFreqPowerEstimators[i].calculatePower(cpuFreqTimes[i]);
                }
                powerMah += power;
            } else {
                Log.w(TAG, "UID " + u.getUid() + " CPU freq # mismatch: Power Profile # "
                        + mPerCpuFreqPowerEstimators.length + " actual # " + cpuFreqTimes.length);
            }
        }

        return powerMah;
    }

@@ -277,7 +369,7 @@ public class CpuPowerCalculator extends PowerCalculator {
     */
    public double calculatePerCpuFreqPowerMah(int cluster, int speedStep,
            long clusterSpeedDurationsMs) {
        return mPerCpuFreqPowerEstimators[cluster][speedStep].calculatePower(
        return mPerCpuFreqPowerEstimatorsByCluster[cluster][speedStep].calculatePower(
                clusterSpeedDurationsMs);
    }
}
Loading