diff options
6 files changed, 170 insertions, 87 deletions
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index 72a6e16c7df5..ce1a16da9e14 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -48,6 +48,7 @@ public abstract class BatteryConsumer { POWER_COMPONENT_SENSORS, POWER_COMPONENT_GNSS, POWER_COMPONENT_SCREEN, + POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, }) @Retention(RetentionPolicy.SOURCE) public static @interface PowerComponent { @@ -65,8 +66,12 @@ public abstract class BatteryConsumer { public static final int POWER_COMPONENT_SENSORS = 9; public static final int POWER_COMPONENT_GNSS = 10; public static final int POWER_COMPONENT_SCREEN = 13; + // Power that is re-attributed to other battery consumers. For example, for System Server + // this represents the power attributed to apps requesting system services. + // The value should be negative or zero. + public static final int POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS = 14; - public static final int POWER_COMPONENT_COUNT = 14; + public static final int POWER_COMPONENT_COUNT = 15; public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999; @@ -236,5 +241,13 @@ public abstract class BatteryConsumer { componentUsageTimeMillis); return (T) this; } + + /** + * Returns the total power accumulated by this builder so far. It may change + * by the time the {@code build()} method is called. + */ + public double getTotalPower() { + return mPowerComponentsBuilder.getTotalPower(); + } } } diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index 1337d558e439..ac2328504f74 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -38,11 +38,7 @@ class PowerComponents { mCustomPowerComponentCount = builder.mCustomPowerComponentCount; mPowerComponents = builder.mPowerComponents; mTimeComponents = builder.mTimeComponents; - double totalPower = 0; - for (int i = mPowerComponents.length - 1; i >= 0; i--) { - totalPower += mPowerComponents[i]; - } - mTotalPowerConsumed = totalPower; + mTotalPowerConsumed = builder.getTotalPower(); } PowerComponents(@NonNull Parcel source) { @@ -264,6 +260,18 @@ class PowerComponents { } /** + * Returns the total power accumulated by this builder so far. It may change + * by the time the {@code build()} method is called. + */ + public double getTotalPower() { + double totalPower = 0; + for (int i = mPowerComponents.length - 1; i >= 0; i--) { + totalPower += mPowerComponents[i]; + } + return totalPower; + } + + /** * Creates a read-only object out of the Builder values. */ @NonNull diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java index 9b88516e547e..4f2f973b51b1 100644 --- a/core/java/com/android/internal/os/BatterySipper.java +++ b/core/java/com/android/internal/os/BatterySipper.java @@ -135,6 +135,11 @@ public class BatterySipper implements Comparable<BatterySipper> { public double systemServiceCpuPowerMah; public double[] customMeasuredPowerMah; + // Power that is re-attributed to other sippers. For example, for System Server + // this represents the power attributed to apps requesting system services. + // The value should be negative or zero. + public double powerReattributedToOtherSippersMah; + // Do not include this sipper in results because it is included // in an aggregate sipper. public boolean isAggregated; @@ -263,6 +268,7 @@ public class BatterySipper implements Comparable<BatterySipper> { } } } + powerReattributedToOtherSippersMah += other.powerReattributedToOtherSippersMah; } /** @@ -281,6 +287,10 @@ public class BatterySipper implements Comparable<BatterySipper> { totalPowerMah += customMeasuredPowerMah[idx]; } } + + // powerAttributedToOtherSippersMah is negative or zero + totalPowerMah = totalPowerMah + powerReattributedToOtherSippersMah; + totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah; return totalPowerMah; diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index 2798b72ab21f..233ba1912dcd 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -70,11 +70,14 @@ public class BatteryUsageStatsProvider { mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile)); mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile)); mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile)); - mPowerCalculators.add(new SystemServicePowerCalculator(mPowerProfile)); mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile)); mPowerCalculators.add(new CustomMeasuredPowerCalculator(mPowerProfile)); - mPowerCalculators.add(new UserPowerCalculator()); + + // It is important that SystemServicePowerCalculator be applied last, + // because it re-attributes some of the power estimated by the other + // calculators. + mPowerCalculators.add(new SystemServicePowerCalculator(mPowerProfile)); } } return mPowerCalculators; @@ -128,7 +131,8 @@ public class BatteryUsageStatsProvider { final long uptimeUs = SystemClock.uptimeMillis() * 1000; final List<PowerCalculator> powerCalculators = getPowerCalculators(); - for (PowerCalculator powerCalculator : powerCalculators) { + for (int i = 0, count = powerCalculators.size(); i < count; i++) { + PowerCalculator powerCalculator = powerCalculators.get(i); powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs, query); } diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java index 955f6afe579c..5c0eeb2f54f9 100644 --- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java +++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java @@ -20,12 +20,12 @@ import android.os.BatteryConsumer; import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; +import android.os.Process; import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.util.Log; import android.util.SparseArray; -import java.util.Arrays; import java.util.List; /** @@ -36,87 +36,112 @@ public class SystemServicePowerCalculator extends PowerCalculator { private static final boolean DEBUG = false; private static final String TAG = "SystemServicePowerCalc"; - private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000; - - private final PowerProfile mPowerProfile; - - // Tracks system server CPU [cluster][speed] power in milliAmp-microseconds - // Data organized like this: + // Power estimators per CPU cluster, per CPU frequency. The array is flattened according + // to this layout: // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...} - private double[] mSystemServicePowerMaUs; + private final UsageBasedPowerEstimator[] mPowerEstimators; public SystemServicePowerCalculator(PowerProfile powerProfile) { - mPowerProfile = powerProfile; + int numFreqs = 0; + final int numCpuClusters = powerProfile.getNumCpuClusters(); + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + numFreqs += powerProfile.getNumSpeedStepsInCpuCluster(cluster); + } + + mPowerEstimators = new UsageBasedPowerEstimator[numFreqs]; + int index = 0; + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + final int numSpeeds = powerProfile.getNumSpeedStepsInCpuCluster(cluster); + for (int speed = 0; speed < numSpeeds; speed++) { + mPowerEstimators[index++] = new UsageBasedPowerEstimator( + powerProfile.getAveragePowerForCpuCore(cluster, speed)); + } + } } @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - calculateSystemServicePower(batteryStats); - super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query); - } + double systemServicePowerMah = calculateSystemServicePower(batteryStats); + final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = + builder.getUidBatteryConsumerBuilders(); + final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get( + Process.SYSTEM_UID); + + if (systemServerConsumer != null) { + systemServicePowerMah = Math.min(systemServicePowerMah, + systemServerConsumer.getTotalPower()); + + // The system server power needs to be adjusted because part of it got + // distributed to applications + systemServerConsumer.setConsumedPower( + BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, + -systemServicePowerMah); + } - @Override - protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES, - calculateSystemServerCpuPowerMah(u)); + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); + if (app != systemServerConsumer) { + final BatteryStats.Uid uid = app.getBatteryStatsUid(); + app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES, + systemServicePowerMah * uid.getProportionalSystemServiceUsage()); + } + } } @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - calculateSystemServicePower(batteryStats); - super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); - } + double systemServicePowerMah = calculateSystemServicePower(batteryStats); + BatterySipper systemServerSipper = null; + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + if (app.getUid() == Process.SYSTEM_UID) { + systemServerSipper = app; + break; + } + } + } - @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - app.systemServiceCpuPowerMah = calculateSystemServerCpuPowerMah(u); + if (systemServerSipper != null) { + systemServicePowerMah = Math.min(systemServicePowerMah, systemServerSipper.sumPower()); + + // The system server power needs to be adjusted because part of it got + // distributed to applications + systemServerSipper.powerReattributedToOtherSippersMah = -systemServicePowerMah; + } + + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + if (app != systemServerSipper) { + final BatteryStats.Uid uid = app.uidObj; + app.systemServiceCpuPowerMah = + systemServicePowerMah * uid.getProportionalSystemServiceUsage(); + } + } + } } - private void calculateSystemServicePower(BatteryStats batteryStats) { + private double calculateSystemServicePower(BatteryStats batteryStats) { final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds(); if (systemServiceTimeAtCpuSpeeds == null) { - return; + return 0; } - if (mSystemServicePowerMaUs == null) { - mSystemServicePowerMaUs = new double[systemServiceTimeAtCpuSpeeds.length]; - } - int index = 0; - final int numCpuClusters = mPowerProfile.getNumCpuClusters(); - for (int cluster = 0; cluster < numCpuClusters; cluster++) { - final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); - for (int speed = 0; speed < numSpeeds; speed++) { - mSystemServicePowerMaUs[index] = - systemServiceTimeAtCpuSpeeds[index] - * mPowerProfile.getAveragePowerForCpuCore(cluster, speed); - index++; - } - } + // TODO(179210707): additionally account for CPU active and per cluster battery use - if (DEBUG) { - Log.d(TAG, "System service power per CPU cluster and frequency:" - + Arrays.toString(mSystemServicePowerMaUs)); + double powerMah = 0; + for (int i = 0; i < mPowerEstimators.length; i++) { + powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i]); } - } - private double calculateSystemServerCpuPowerMah(BatteryStats.Uid u) { - double cpuPowerMaUs = 0; - final double proportionalUsage = u.getProportionalSystemServiceUsage(); - if (proportionalUsage > 0 && mSystemServicePowerMaUs != null) { - for (int i = 0; i < mSystemServicePowerMaUs.length; i++) { - cpuPowerMaUs += mSystemServicePowerMaUs[i] * proportionalUsage; - } + if (DEBUG) { + Log.d(TAG, "System service power:" + powerMah); } - return cpuPowerMaUs / MICROSEC_IN_HR; - } - @Override - public void reset() { - mSystemServicePowerMaUs = null; + 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 24741fe110ce..dfbf28b286c6 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -18,9 +18,16 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyInt; +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; @@ -39,34 +46,48 @@ import java.util.Collection; @RunWith(AndroidJUnit4.class) public class SystemServicePowerCalculatorTest { - private static final double PRECISION = 0.0000001; + private static final double PRECISION = 0.000001; @Rule - public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); - + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720) + .setNumCpuClusters(2) + .setNumSpeedStepsInCpuCluster(0, 2) + .setNumSpeedStepsInCpuCluster(1, 2) + .setAveragePowerForCpuCluster(0, 360) + .setAveragePowerForCpuCluster(1, 480) + .setAveragePowerForCpuCore(0, 0, 300) + .setAveragePowerForCpuCore(0, 1, 400) + .setAveragePowerForCpuCore(1, 0, 500) + .setAveragePowerForCpuCore(1, 1, 600); + + private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider; 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(); mMockBatteryStats = mStatsRule.getBatteryStats() .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader) .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader) - .setUserInfoProvider(new MockUserInfoProvider()); + .setUserInfoProvider(mMockUserInfoProvider); } @Test - public void testCalculateApp() { - // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total + 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, 70000, 80000, 90000}, - new long[] {20000, 30000, 40000, 50000, 60000, 70000, 80000}); + new long[] {30000, 40000, 50000, 60000}, + new long[] {20000, 30000, 40000, 50000}); mMockCpuUidFreqTimeReader.setSystemServerCpuTimes( - new long[] {10000, 20000, 30000, 40000, 50000, 60000, 70000} + new long[] {10000, 20000, 30000, 40000} ); mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false); @@ -101,14 +122,17 @@ public class SystemServicePowerCalculatorTest { SystemServicePowerCalculator calculator = new SystemServicePowerCalculator( mStatsRule.getPowerProfile()); - mStatsRule.apply(calculator); + mStatsRule.apply(new FakeCpuPowerCalculator(), calculator); assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid1) .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) - .isWithin(PRECISION).of(0.00016269); + .isWithin(PRECISION).of(1.888888); assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid2) .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) - .isWithin(PRECISION).of(0.00146426); + .isWithin(PRECISION).of(17.0); + assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID) + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS)) + .isWithin(PRECISION).of(-18.888888); } private static class MockKernelCpuUidFreqTimeReader extends @@ -137,7 +161,7 @@ public class SystemServicePowerCalculatorTest { } private static class MockSystemServerCpuThreadReader extends SystemServerCpuThreadReader { - private SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes(); + private final SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes(); MockSystemServerCpuThreadReader() { super(null); @@ -154,16 +178,15 @@ public class SystemServicePowerCalculatorTest { } } - private static class MockUserInfoProvider extends BatteryStatsImpl.UserInfoProvider { - @Nullable - @Override - protected int[] getUserIds() { - return new int[0]; - } - + private static class FakeCpuPowerCalculator extends PowerCalculator { @Override - public boolean exists(int userId) { - return true; + 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); + } } } } |