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

Commit 02bc36cb authored by Michael Wachenschwanz's avatar Michael Wachenschwanz
Browse files

Utilize measured cpu batteryconsumption in CpuPowerCalculator

Fixes: 180079165
Test: atest CpuPowerCalculator

Change-Id: I6a2cb99e229c86a04fbc9630ecab0a6784387285
parent 68b8157e
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -994,6 +994,15 @@ public abstract class BatteryStats implements Parcelable {
         */
        public abstract long getScreenOnMeasuredBatteryConsumptionUC();

        /**
         * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, 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) used by this uid for each
         * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
@@ -2520,6 +2529,15 @@ 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.
     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
     *
     * {@hide}
     */
    public abstract long getCpuMeasuredBatteryConsumptionUC();

    /**
     * Returns the battery consumption (in microcoulombs) that each
     * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
+10 −0
Original line number Diff line number Diff line
@@ -6976,6 +6976,11 @@ public class BatteryStatsImpl extends BatteryStats {
        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE);
    }
    @Override
    public long getCpuMeasuredBatteryConsumptionUC() {
        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
    }
    /**
     * Returns the consumption (in microcoulombs) that the given standard power bucket consumed.
     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable
@@ -8482,6 +8487,11 @@ public class BatteryStatsImpl extends BatteryStats {
            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
        }
        @Override
        public long getCpuMeasuredBatteryConsumptionUC() {
            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
        }
        void initNetworkActivityLocked() {
            detachIfNotNull(mNetworkByteActivityCounters);
            mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+56 −42
Original line number Diff line number Diff line
@@ -85,12 +85,14 @@ public class CpuPowerCalculator extends PowerCalculator {
                builder.getUidBatteryConsumerBuilders();
        for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
            final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
            calculateApp(app, app.getBatteryStatsUid(), result);
            calculateApp(app, app.getBatteryStatsUid(), query, result);
        }
    }

    private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, Result result) {
        calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED, result);
    private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
            BatteryUsageStatsQuery query, Result result) {
        calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED,
                query.shouldForceUsePowerProfileModel(), result);

        app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah)
                .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, result.durationMs)
@@ -112,7 +114,7 @@ public class CpuPowerCalculator extends PowerCalculator {
    }

    private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) {
        calculatePowerAndDuration(u, statsType, result);
        calculatePowerAndDuration(u, statsType, false, result);

        app.cpuPowerMah = result.powerMah;
        app.cpuTimeMs = result.durationMs;
@@ -120,46 +122,16 @@ public class CpuPowerCalculator extends PowerCalculator {
        app.packageWithHighestDrain = result.packageWithHighestDrain;
    }

    private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType, Result result) {
    private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType,
            boolean forceUsePowerProfileModel, Result result) {
        long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;

        // Constant battery drain when CPU is active
        double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());

        // 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]);
                    powerMah += power;
                    if (DEBUG) {
                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
                                + " clusterTimeMs=" + cpuClusterTimes[cluster]
                                + " power=" + formatCharge(power));
                    }
                }
        final double powerMah;
        final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC();
        if (forceUsePowerProfileModel || consumptionUC == BatteryStats.POWER_DATA_UNAVAILABLE) {
            powerMah = calculateUidModeledPowerMah(u, statsType);
        } else {
                Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
                        + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
            }
        }

        // 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));
                }
                powerMah += power;
            }
            powerMah = uCtoMah(consumptionUC);
        }

        if (DEBUG && (durationMs != 0 || powerMah != 0)) {
@@ -208,6 +180,48 @@ public class CpuPowerCalculator extends PowerCalculator {
        result.packageWithHighestDrain = packageWithHighestDrain;
    }

    private double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
        // Constant battery drain when CPU is active
        double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());

        // 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]);
                    powerMah += power;
                    if (DEBUG) {
                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
                                + " clusterTimeMs=" + cpuClusterTimes[cluster]
                                + " power=" + formatCharge(power));
                    }
                }
            } else {
                Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
                        + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
            }
        }

        // 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));
                }
                powerMah += power;
            }
        }
        return powerMah;
    }

    /**
     * Calculates active CPU power consumption.
     *
+78 −8
Original line number Diff line number Diff line
@@ -19,18 +19,22 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.os.BatteryConsumer;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.power.MeasuredEnergyStats;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -81,6 +85,10 @@ public class CpuPowerCalculatorTest {
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        final boolean[] supportedPowerBuckets =
                new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
        supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;

        mStatsRule.getBatteryStats()
                .setUserInfoProvider(mMockUserInfoProvider)
                .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
@@ -88,7 +96,8 @@ public class CpuPowerCalculatorTest {
                .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
                .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
                .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
                .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
                .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader)
                .initMeasuredEnergyStatsLocked(supportedPowerBuckets, 0);
    }

    @Test
@@ -103,28 +112,28 @@ public class CpuPowerCalculatorTest {

        // User/System CPU time
        doAnswer(invocation -> {
            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
            // User/system time in microseconds
            callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000});
            callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000});
            return null;
        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(any());
        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());

        // Active CPU time
        doAnswer(invocation -> {
            final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
            final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
            callback.onUidCpuTime(APP_UID1, 1111L);
            callback.onUidCpuTime(APP_UID2, 3333L);
            return null;
        }).when(mMockKerneCpuUidActiveTimeReader).readDelta(any());
        }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());

        // Per-cluster CPU time
        doAnswer(invocation -> {
            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
            callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
            callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
            return null;
        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(any());
        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());

        mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);

@@ -134,7 +143,8 @@ public class CpuPowerCalculatorTest {
        CpuPowerCalculator calculator =
                new CpuPowerCalculator(mStatsRule.getPowerProfile());

        mStatsRule.apply(calculator);
        mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
                calculator);

        UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
        assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
@@ -150,4 +160,64 @@ public class CpuPowerCalculatorTest {
                .isWithin(PRECISION).of(2.672322);
        assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
    }

    @Test
    public void testMeasuredEnergyBasedModel() {
        when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);

        when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000});
        when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000});

        when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);

        // User/System CPU time
        doAnswer(invocation -> {
            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
            // User/system time in microseconds
            callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000});
            callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000});
            return null;
        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());

        // Active CPU time
        doAnswer(invocation -> {
            final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
            callback.onUidCpuTime(APP_UID1, 1111L);
            callback.onUidCpuTime(APP_UID2, 3333L);
            return null;
        }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());

        // Per-cluster CPU time
        doAnswer(invocation -> {
            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
            callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
            callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
            return null;
        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());

        final long[] clusterChargesUC = new long[]{13577531, 24688642};
        mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, clusterChargesUC);

        mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("foo").addCpuTimeLocked(4321, 1234);
        mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("bar").addCpuTimeLocked(5432, 2345);

        CpuPowerCalculator calculator =
                new CpuPowerCalculator(mStatsRule.getPowerProfile());

        mStatsRule.apply(calculator);

        UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
        assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
                .isEqualTo(3333);
        assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
                .isWithin(PRECISION).of(3.18877);
        assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar");

        UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
        assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
                .isEqualTo(7777);
        assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
                .isWithin(PRECISION).of(7.44072);
        assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
    }
}