diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index 0d041c4440d21..e693d9dd9a7d6 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -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()); diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java index a26abc2ffab09..b4a2b637cae82 100644 --- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java +++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java @@ -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 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 sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray 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; } } diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java index cd45060b7cc42..a36d9fe94b467 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -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 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 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}); - public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) { - mThreadTimes.threadCpuTimesUs = threadCpuTimesUs; - mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs; - } + when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false); - @Override - public SystemServiceCpuThreadTimes readDelta() { - return mThreadTimes; - } - } + // User/System CPU time in microseconds + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback 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()); - 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); - } - } + // Active CPU time in milliseconds + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback 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 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); + + int transactionCode = 42; + + Collection 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); + + 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(); } }