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

Commit 16d1188b authored by Dmitri Plotnikov's avatar Dmitri Plotnikov Committed by Android (Google) Code Review
Browse files

Merge "Fix system service power calculation when measured energy is available" into sc-dev

parents d55565dc 1643f794
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -201,7 +201,10 @@ public class CpuPowerCalculator extends PowerCalculator {
        result.packageWithHighestDrain = packageWithHighestDrain;
    }

    private double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
    /**
     * Calculates CPU power consumed by the specified app, using the PowerProfile model.
     */
    public double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
        // Constant battery drain when CPU is active
        double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());

+51 −7
Original line number Diff line number Diff line
@@ -40,8 +40,10 @@ public class SystemServicePowerCalculator extends PowerCalculator {
    // to this layout:
    // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...}
    private final UsageBasedPowerEstimator[] mPowerEstimators;
    private final CpuPowerCalculator mCpuPowerCalculator;

    public SystemServicePowerCalculator(PowerProfile powerProfile) {
        mCpuPowerCalculator = new CpuPowerCalculator(powerProfile);
        int numFreqs = 0;
        final int numCpuClusters = powerProfile.getNumCpuClusters();
        for (int cluster = 0; cluster < numCpuClusters; cluster++) {
@@ -62,7 +64,22 @@ public class SystemServicePowerCalculator extends PowerCalculator {
    @Override
    public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
        double systemServicePowerMah = calculateSystemServicePower(batteryStats);
        final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID);
        if (systemUid == null) {
            return;
        }

        final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC();
        final int powerModel = getPowerModel(consumptionUC, query);

        double systemServicePowerMah;
        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
            systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats,
                    systemUid, consumptionUC);
        } else {
            systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
        }

        final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
                builder.getUidBatteryConsumerBuilders();
        final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get(
@@ -76,7 +93,7 @@ public class SystemServicePowerCalculator extends PowerCalculator {
            // distributed to applications
            systemServerConsumer.setConsumedPower(
                    BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
                    -systemServicePowerMah);
                    -systemServicePowerMah, powerModel);
        }

        for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
@@ -84,7 +101,8 @@ public class SystemServicePowerCalculator extends PowerCalculator {
            if (app != systemServerConsumer) {
                final BatteryStats.Uid uid = app.getBatteryStatsUid();
                app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
                        systemServicePowerMah * uid.getProportionalSystemServiceUsage());
                        systemServicePowerMah * uid.getProportionalSystemServiceUsage(),
                        powerModel);
            }
        }

@@ -102,7 +120,20 @@ public class SystemServicePowerCalculator extends PowerCalculator {
    public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
            long rawRealtimeUs, long rawUptimeUs, int statsType,
            SparseArray<UserHandle> asUsers) {
        double systemServicePowerMah = calculateSystemServicePower(batteryStats);
        final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID);
        if (systemUid == null) {
            return;
        }

        final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC();
        double systemServicePowerMah;
        if (getPowerModel(consumptionUC) == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
            systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats,
                    systemUid, consumptionUC);
        } else {
            systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
        }

        BatterySipper systemServerSipper = null;
        for (int i = sippers.size() - 1; i >= 0; i--) {
            final BatterySipper app = sippers.get(i);
@@ -134,7 +165,21 @@ public class SystemServicePowerCalculator extends PowerCalculator {
        }
    }

    private double calculateSystemServicePower(BatteryStats batteryStats) {
    private double calculatePowerUsingMeasuredConsumption(BatteryStats batteryStats,
            BatteryStats.Uid systemUid, long consumptionUC) {
        // Use the PowerProfile based model to estimate the ratio between the power consumed
        // while handling incoming binder calls and the entire System UID power consumption.
        // Apply that ratio to the _measured_ system UID power consumption to get a more
        // accurate estimate of the power consumed by incoming binder calls.
        final double systemServiceModeledPowerMah = calculatePowerUsingPowerProfile(batteryStats);
        final double systemUidModeledPowerMah = mCpuPowerCalculator.calculateUidModeledPowerMah(
                systemUid, BatteryStats.STATS_SINCE_CHARGED);

        return uCtoMah(consumptionUC) * systemServiceModeledPowerMah
                / systemUidModeledPowerMah;
    }

    private double calculatePowerUsingPowerProfile(BatteryStats batteryStats) {
        final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds();
        if (systemServiceTimeAtCpuSpeeds == null) {
            return 0;
@@ -145,13 +190,12 @@ public class SystemServicePowerCalculator extends PowerCalculator {
        double powerMah = 0;
        final int size = Math.min(mPowerEstimators.length, systemServiceTimeAtCpuSpeeds.length);
        for (int i = 0; i < size; i++) {
            powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i]);
            powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i] / 1000);
        }

        if (DEBUG) {
            Log.d(TAG, "System service power:" + powerMah);
        }

        return powerMah;
    }
}
+131 −100
Original line number Diff line number Diff line
@@ -18,25 +18,28 @@ 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.BatteryStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Binder;
import android.os.Process;
import android.os.UidBatteryConsumer;

import androidx.annotation.Nullable;
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;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.io.IOException;
import java.util.ArrayList;
@@ -47,6 +50,8 @@ import java.util.Collection;
public class SystemServicePowerCalculatorTest {

    private static final double PRECISION = 0.000001;
    private static final int APP_UID1 = 100;
    private static final int APP_UID2 = 200;

    @Rule
    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
@@ -61,73 +66,52 @@ public class SystemServicePowerCalculatorTest {
            .setAveragePowerForCpuCore(1, 0, 500)
            .setAveragePowerForCpuCore(1, 1, 600);

    @Mock
    private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider;
    @Mock
    private KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader mMockKernelCpuUidClusterTimeReader;
    @Mock
    private KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
    @Mock
    private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mMockKernelCpuUidUserSysTimeReader;
    @Mock
    private KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader mMockKerneCpuUidActiveTimeReader;
    @Mock
    private SystemServerCpuThreadReader mMockSystemServerCpuThreadReader;

    private final KernelCpuSpeedReader[] mMockKernelCpuSpeedReaders = new KernelCpuSpeedReader[]{
            mock(KernelCpuSpeedReader.class),
            mock(KernelCpuSpeedReader.class),
    };

    private MockBatteryStatsImpl mMockBatteryStats;
    private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
    private MockSystemServerCpuThreadReader mMockSystemServerCpuThreadReader;

    @Before
    public void setUp() throws IOException {
        mMockUserInfoProvider = mock(BatteryStatsImpl.UserInfoProvider.class);
        mMockSystemServerCpuThreadReader = new MockSystemServerCpuThreadReader();
        mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader();
        MockitoAnnotations.initMocks(this);
        mMockBatteryStats = mStatsRule.getBatteryStats()
                .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader)
                .setUserInfoProvider(mMockUserInfoProvider)
                .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
                .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
                .setUserInfoProvider(mMockUserInfoProvider);
                .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
                .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
                .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
                .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
    }

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

        // Test Power Profile has two CPU clusters with 2 speeds each, thus 4 freq times total
        mMockSystemServerCpuThreadReader.setCpuTimes(
                new long[] {30000, 40000, 50000, 60000},
                new long[] {20000, 30000, 40000, 50000});

        mMockCpuUidFreqTimeReader.setSystemServerCpuTimes(
                new long[] {10000, 20000, 30000, 40000}
        );

        mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false, null);

        int workSourceUid1 = 100;
        int workSourceUid2 = 200;
        int transactionCode = 42;

        Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
        BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(workSourceUid1,
                Binder.class, transactionCode, true /*screenInteractive */);
        stat1.incrementalCallCount = 100;
        stat1.recordedCallCount = 100;
        stat1.cpuTimeMicros = 1000000;
        callStats.add(stat1);

        mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats);

        callStats.clear();
        BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2,
                Binder.class, transactionCode, true /*screenInteractive */);
        stat2.incrementalCallCount = 100;
        stat2.recordedCallCount = 100;
        stat2.cpuTimeMicros = 9000000;
        callStats.add(stat2);

        mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats);

        mMockBatteryStats.updateSystemServiceCallStats();
        mMockBatteryStats.updateSystemServerThreadStats();
        prepareBatteryStats(null);

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

        mStatsRule.apply(new FakeCpuPowerCalculator(), calculator);
        mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator);

        assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid1)
        assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1)
                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
                .isWithin(PRECISION).of(1.888888);
        assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid2)
        assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2)
                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
                .isWithin(PRECISION).of(17.0);
        assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID)
@@ -141,58 +125,105 @@ public class SystemServicePowerCalculatorTest {
                .isWithin(PRECISION).of(18.888888);
    }

    private static class MockKernelCpuUidFreqTimeReader extends
            KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader {
        private long[] mSystemServerCpuTimes;
    @Test
    public void testMeasuredEnergyBasedModel() {
        final boolean[] supportedPowerBuckets =
                new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
        supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
        mStatsRule.getBatteryStats()
                .initMeasuredEnergyStatsLocked(supportedPowerBuckets, new String[0]);

        MockKernelCpuUidFreqTimeReader() {
            super(/*throttle */false);
        }
        prepareBatteryStats(new long[]{50000000, 100000000});

        void setSystemServerCpuTimes(long[] systemServerCpuTimes) {
            mSystemServerCpuTimes = systemServerCpuTimes;
        }
        SystemServicePowerCalculator calculator = new SystemServicePowerCalculator(
                mStatsRule.getPowerProfile());

        @Override
        public boolean perClusterTimesAvailable() {
            return true;
        }
        mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator);

        @Override
        public void readDelta(boolean forcedRead, @Nullable Callback<long[]> cb) {
            if (cb != null) {
                cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes);
            }
        }
        assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1)
                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
                .isWithin(PRECISION).of(1.979351);
        assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2)
                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
                .isWithin(PRECISION).of(17.814165);
        assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID)
                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS))
                .isWithin(PRECISION).of(-19.793517);
        assertThat(mStatsRule.getDeviceBatteryConsumer()
                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
                .isWithin(PRECISION).of(19.793517);
        assertThat(mStatsRule.getAppsBatteryConsumer()
                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
                .isWithin(PRECISION).of(19.793517);
    }

    private static class MockSystemServerCpuThreadReader extends SystemServerCpuThreadReader {
        private final SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes();
    private void prepareBatteryStats(long[] clusterChargesUc) {
        when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);

        MockSystemServerCpuThreadReader() {
            super(null);
        }
        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 in microseconds
        doAnswer(invocation -> {
            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
            callback.onUidCpuTime(APP_UID1, new long[]{1_000_000, 2_000_000});
            callback.onUidCpuTime(APP_UID2, new long[]{3_000_000, 4_000_000});
            callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{60_000_000, 80_000_000});
            return null;
        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());

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

        // Per-cluster CPU time in milliseconds
        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});
            callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{50_000, 80_000});
            return null;
        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());

        // System service CPU time
        final SystemServerCpuThreadReader.SystemServiceCpuThreadTimes threadTimes =
                new SystemServerCpuThreadReader.SystemServiceCpuThreadTimes();
        threadTimes.binderThreadCpuTimesUs =
                new long[]{20_000_000, 30_000_000, 40_000_000, 50_000_000};

        when(mMockSystemServerCpuThreadReader.readDelta()).thenReturn(threadTimes);

        public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) {
            mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
            mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
        }
        int transactionCode = 42;

        @Override
        public SystemServiceCpuThreadTimes readDelta() {
            return mThreadTimes;
        }
    }
        Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
        BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(APP_UID1,
                Binder.class, transactionCode, true /*screenInteractive */);
        stat1.incrementalCallCount = 100;
        stat1.recordedCallCount = 100;
        stat1.cpuTimeMicros = 1_000_000;
        callStats.add(stat1);

    private static class FakeCpuPowerCalculator extends PowerCalculator {
        @Override
        protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
                long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
            if (u.getUid() == Process.SYSTEM_UID) {
                // SystemServer must be attributed at least as much power as the total
                // of all system services requested by apps.
                app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 1000000);
            }
        }
        mMockBatteryStats.noteBinderCallStats(APP_UID1, 100, callStats);

        callStats.clear();
        BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(APP_UID2,
                Binder.class, transactionCode, true /*screenInteractive */);
        stat2.incrementalCallCount = 100;
        stat2.recordedCallCount = 100;
        stat2.cpuTimeMicros = 9_000_000;
        callStats.add(stat2);

        mMockBatteryStats.noteBinderCallStats(APP_UID2, 100, callStats);

        mMockBatteryStats.updateCpuTimeLocked(true, true, clusterChargesUc);

        mMockBatteryStats.prepareForDumpLocked();
    }
}