Loading core/java/com/android/internal/os/CpuPowerCalculator.java +4 −1 Original line number Diff line number Diff line Loading @@ -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()); Loading core/java/com/android/internal/os/SystemServicePowerCalculator.java +51 −7 Original line number Diff line number Diff line Loading @@ -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++) { Loading @@ -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( Loading @@ -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--) { Loading @@ -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); } } Loading @@ -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); Loading Loading @@ -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; Loading @@ -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; } } core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +131 −100 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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() Loading @@ -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) Loading @@ -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(); } } Loading
core/java/com/android/internal/os/CpuPowerCalculator.java +4 −1 Original line number Diff line number Diff line Loading @@ -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()); Loading
core/java/com/android/internal/os/SystemServicePowerCalculator.java +51 −7 Original line number Diff line number Diff line Loading @@ -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++) { Loading @@ -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( Loading @@ -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--) { Loading @@ -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); } } Loading @@ -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); Loading Loading @@ -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; Loading @@ -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; } }
core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +131 −100 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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() Loading @@ -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) Loading @@ -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(); } }