diff options
7 files changed, 243 insertions, 66 deletions
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index f2b466d7fa5d..72a6e16c7df5 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -47,6 +47,7 @@ public abstract class BatteryConsumer { POWER_COMPONENT_SYSTEM_SERVICES, POWER_COMPONENT_SENSORS, POWER_COMPONENT_GNSS, + POWER_COMPONENT_SCREEN, }) @Retention(RetentionPolicy.SOURCE) public static @interface PowerComponent { @@ -63,8 +64,9 @@ public abstract class BatteryConsumer { public static final int POWER_COMPONENT_MOBILE_RADIO = 8; public static final int POWER_COMPONENT_SENSORS = 9; public static final int POWER_COMPONENT_GNSS = 10; + public static final int POWER_COMPONENT_SCREEN = 13; - public static final int POWER_COMPONENT_COUNT = 11; + public static final int POWER_COMPONENT_COUNT = 14; public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999; @@ -85,6 +87,7 @@ public abstract class BatteryConsumer { TIME_COMPONENT_MOBILE_RADIO, TIME_COMPONENT_SENSORS, TIME_COMPONENT_GNSS, + TIME_COMPONENT_SCREEN, }) @Retention(RetentionPolicy.SOURCE) public static @interface TimeComponent { @@ -101,8 +104,9 @@ public abstract class BatteryConsumer { public static final int TIME_COMPONENT_MOBILE_RADIO = 8; public static final int TIME_COMPONENT_SENSORS = 9; public static final int TIME_COMPONENT_GNSS = 10; + public static final int TIME_COMPONENT_SCREEN = 13; - public static final int TIME_COMPONENT_COUNT = 11; + public static final int TIME_COMPONENT_COUNT = 14; public static final int FIRST_CUSTOM_TIME_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_TIME_COMPONENT_ID = 9999; diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index d1ca6a560c3f..a828077ac055 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -99,12 +99,13 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela private boolean mExcludeFromBatteryUsageStats; public Builder(int customPowerComponentCount, int customTimeComponentCount, - BatteryStats.Uid batteryStatsUid) { + @NonNull BatteryStats.Uid batteryStatsUid) { super(customPowerComponentCount, customTimeComponentCount); mBatteryStatsUid = batteryStatsUid; mUid = batteryStatsUid.getUid(); } + @NonNull public BatteryStats.Uid getBatteryStatsUid() { return mBatteryStatsUid; } diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java index 16b6f3a155f8..13130902feb8 100644 --- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java +++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java @@ -83,7 +83,7 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { private double getMeasuredOrEstimatedPower(long measuredEnergyUJ, long durationMs) { if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { - return mAhToUJ(measuredEnergyUJ); + return uJtoMah(measuredEnergyUJ); } else { return mPowerEstimator.calculatePower(durationMs); } diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java index 141363fa8b25..7c45cc09e18c 100644 --- a/core/java/com/android/internal/os/PowerCalculator.java +++ b/core/java/com/android/internal/os/PowerCalculator.java @@ -143,7 +143,11 @@ public abstract class PowerCalculator { return String.format(Locale.ENGLISH, format, power); } - static double mAhToUJ(long energyUJ) { + static double uJtoMah(long energyUJ) { + if (energyUJ == 0) { + return 0; + } + // TODO(b/173765509): Convert properly. This is mJ / V * (h/3600s) = mAh with V = 3.7 fixed. // Leaving for later since desired units of energy have yet to be decided return energyUJ / 1000.0 / 3.7 / 3600; diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index bb1222eac11c..c1dd7ce662f7 100644 --- a/core/java/com/android/internal/os/ScreenPowerCalculator.java +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -21,7 +21,7 @@ import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.SystemBatteryConsumer; -import android.os.SystemClock; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.text.format.DateUtils; import android.util.Slog; @@ -39,9 +39,17 @@ public class ScreenPowerCalculator extends PowerCalculator { private static final String TAG = "ScreenPowerCalculator"; private static final boolean DEBUG = BatteryStatsHelper.DEBUG; + // Minimum amount of time the screen should be on to start smearing drain to apps + public static final long MIN_ACTIVE_TIME_FOR_SMEARING = 10 * DateUtils.MINUTE_IN_MILLIS; + private final UsageBasedPowerEstimator mScreenOnPowerEstimator; private final UsageBasedPowerEstimator mScreenFullPowerEstimator; + private static class PowerAndDuration { + public long durationMs; + public double powerMah; + } + public ScreenPowerCalculator(PowerProfile powerProfile) { mScreenOnPowerEstimator = new UsageBasedPowerEstimator( powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON)); @@ -52,17 +60,40 @@ public class ScreenPowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - final long durationMs = computeDuration(batteryStats, rawRealtimeUs, - BatteryStats.STATS_SINCE_CHARGED); - final double powerMah = computePower(batteryStats, rawRealtimeUs, - BatteryStats.STATS_SINCE_CHARGED, durationMs); - if (powerMah != 0) { - builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah); + final PowerAndDuration totalPowerAndDuration = new PowerAndDuration(); + final boolean forceUsePowerProfileModel = (query.getFlags() + & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL) != 0; + + final boolean useEnergyData = calculateTotalDurationAndPower(totalPowerAndDuration, + batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, + forceUsePowerProfileModel); + + builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, + totalPowerAndDuration.durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, + totalPowerAndDuration.powerMah); + + // Now deal with each app's UidBatteryConsumer. The results are stored in the + // BatteryConsumer.POWER_COMPONENT_SCREEN power component, which is considered smeared, + // but the method depends on the data source. + final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = + builder.getUidBatteryConsumerBuilders(); + if (useEnergyData) { + final PowerAndDuration appPowerAndDuration = new PowerAndDuration(); + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); + calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.getBatteryStatsUid(), + rawRealtimeUs); + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, + appPowerAndDuration.durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, + appPowerAndDuration.powerMah); + } + } else { + smearScreenBatteryDrain(uidBatteryConsumerBuilders, totalPowerAndDuration, + rawRealtimeUs); } - // TODO(b/178140704): Attribute *measured* total usage for BatteryUsageStats. - // TODO(b/178140704): Attribute (measured/smeared) usage *per app* for BatteryUsageStats. } /** @@ -71,51 +102,79 @@ public class ScreenPowerCalculator extends PowerCalculator { @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - - final long energyUJ = batteryStats.getScreenOnEnergy(); - final boolean isMeasuredDataAvailable = energyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE; - - final long durationMs = computeDuration(batteryStats, rawRealtimeUs, statsType); - final double powerMah = getMeasuredOrComputedPower( - energyUJ, batteryStats, rawRealtimeUs, statsType, durationMs); - if (powerMah == 0) { + final PowerAndDuration totalPowerAndDuration = new PowerAndDuration(); + final boolean useEnergyData = calculateTotalDurationAndPower(totalPowerAndDuration, + batteryStats, rawRealtimeUs, statsType, false); + if (totalPowerAndDuration.powerMah == 0) { return; } // First deal with the SCREEN BatterySipper (since we need this for smearing over apps). final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0); - bs.usagePowerMah = powerMah; - bs.usageTimeMs = durationMs; + bs.usagePowerMah = totalPowerAndDuration.powerMah; + bs.usageTimeMs = totalPowerAndDuration.durationMs; bs.sumPower(); sippers.add(bs); // Now deal with each app's BatterySipper. The results are stored in the screenPowerMah // field, which is considered smeared, but the method depends on the data source. - if (isMeasuredDataAvailable) { - super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + if (useEnergyData) { + final PowerAndDuration appPowerAndDuration = new PowerAndDuration(); + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.uidObj, rawRealtimeUs); + app.screenPowerMah = appPowerAndDuration.powerMah; + } + } } else { - smearScreenBatterySipper(sippers, bs); + smearScreenBatterySipper(sippers, bs, rawRealtimeUs); } } - @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + /** + * Stores duration and power information in totalPowerAndDuration and returns true if measured + * energy data is available and should be used by the model. + */ + private boolean calculateTotalDurationAndPower(PowerAndDuration totalPowerAndDuration, + BatteryStats batteryStats, long rawRealtimeUs, int statsType, + boolean forceUsePowerProfileModel) { + totalPowerAndDuration.durationMs = calculateDuration(batteryStats, rawRealtimeUs, + statsType); + + if (!forceUsePowerProfileModel) { + final long energyUJ = batteryStats.getScreenOnEnergy(); + if (energyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { + totalPowerAndDuration.powerMah = uJtoMah(energyUJ); + return true; + } + } + + totalPowerAndDuration.powerMah = calculateTotalPowerFromBrightness(batteryStats, + rawRealtimeUs, statsType, totalPowerAndDuration.durationMs); + return false; + } + + private void calculateAppUsingMeasuredEnergy(PowerAndDuration appPowerAndDuration, + BatteryStats.Uid u, long rawRealtimeUs) { + appPowerAndDuration.durationMs = getProcessForegroundTimeMs(u, rawRealtimeUs); + final long energyUJ = u.getScreenOnEnergy(); if (energyUJ < 0) { Slog.wtf(TAG, "Screen energy not supported, so calculateApp shouldn't de called"); + appPowerAndDuration.powerMah = 0; return; } - if (energyUJ == 0) return; - app.screenPowerMah = mAhToUJ(u.getScreenOnEnergy()); + + appPowerAndDuration.powerMah = uJtoMah(energyUJ); } - private long computeDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) { + private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) { return batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000; } - private double computePower(BatteryStats batteryStats, long rawRealtimeUs, int statsType, - long durationMs) { + private double calculateTotalPowerFromBrightness(BatteryStats batteryStats, long rawRealtimeUs, + int statsType, long durationMs) { double power = mScreenOnPowerEstimator.calculatePower(durationMs); for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { final long brightnessTime = @@ -131,35 +190,25 @@ public class ScreenPowerCalculator extends PowerCalculator { return power; } - private double getMeasuredOrComputedPower(long measuredEnergyUJ, - BatteryStats batteryStats, long rawRealtimeUs, int statsType, long durationMs) { - - if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { - return mAhToUJ(measuredEnergyUJ); - } else { - return computePower(batteryStats, rawRealtimeUs, statsType, durationMs); - } - } - /** * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity * time, and store this in the {@link BatterySipper#screenPowerMah} field. */ @VisibleForTesting - public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) { - + public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper, + long rawRealtimeUs) { long totalActivityTimeMs = 0; final SparseLongArray activityTimeArray = new SparseLongArray(); for (int i = sippers.size() - 1; i >= 0; i--) { final BatteryStats.Uid uid = sippers.get(i).uidObj; if (uid != null) { - final long timeMs = getProcessForegroundTimeMs(uid); + final long timeMs = getProcessForegroundTimeMs(uid, rawRealtimeUs); activityTimeArray.put(uid.getUid(), timeMs); totalActivityTimeMs += timeMs; } } - if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) { + if (screenSipper != null && totalActivityTimeMs >= MIN_ACTIVE_TIME_FOR_SMEARING) { final double totalScreenPowerMah = screenSipper.totalPowerMah; for (int i = sippers.size() - 1; i >= 0; i--) { final BatterySipper sipper = sippers.get(i); @@ -170,10 +219,37 @@ public class ScreenPowerCalculator extends PowerCalculator { } } + /** + * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity + * time, and store this in the {@link BatterySipper#screenPowerMah} field. + */ + private void smearScreenBatteryDrain( + SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders, + PowerAndDuration totalPowerAndDuration, long rawRealtimeUs) { + long totalActivityTimeMs = 0; + final SparseLongArray activityTimeArray = new SparseLongArray(); + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + final BatteryStats.Uid uid = uidBatteryConsumerBuilders.valueAt(i).getBatteryStatsUid(); + final long timeMs = getProcessForegroundTimeMs(uid, rawRealtimeUs); + activityTimeArray.put(uid.getUid(), timeMs); + totalActivityTimeMs += timeMs; + } + + if (totalActivityTimeMs >= MIN_ACTIVE_TIME_FOR_SMEARING) { + final double totalScreenPowerMah = totalPowerAndDuration.powerMah; + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); + final long durationMs = activityTimeArray.get(app.getUid(), 0); + final double powerMah = totalScreenPowerMah * durationMs / totalActivityTimeMs; + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, powerMah); + } + } + } + /** Get the minimum of the uid's ForegroundActivity time and its TOP time. */ @VisibleForTesting - public long getProcessForegroundTimeMs(BatteryStats.Uid uid) { - final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000; + public long getProcessForegroundTimeMs(BatteryStats.Uid uid, long rawRealTimeUs) { final int[] foregroundTypes = {BatteryStats.Uid.PROCESS_STATE_TOP}; long timeUs = 0; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java index d2107eaefefc..08205b4d143c 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java @@ -218,7 +218,7 @@ public class BatteryStatsHelperTest extends TestCase { sippers.add(sipperBg); sippers.add(sipperFg); - spc.smearScreenBatterySipper(sippers, mScreenBatterySipper); + spc.smearScreenBatterySipper(sippers, mScreenBatterySipper, 0); assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0); assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0); @@ -253,7 +253,7 @@ public class BatteryStatsHelperTest extends TestCase { doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP), anyLong(), anyInt()); - final long time = spc.getProcessForegroundTimeMs(mUid); + final long time = spc.getProcessForegroundTimeMs(mUid, 1000); assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS); } @@ -296,7 +296,7 @@ public class BatteryStatsHelperTest extends TestCase { doReturn(uidCode).when(sipper).getUid(); if (!isUidNull) { final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS); - doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid)); + doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid), anyLong()); doReturn(uidCode).when(uid).getUid(); sipper.uidObj = uid; } diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java index e43caa37f711..717fac0c7bf8 100644 --- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java @@ -18,8 +18,12 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; +import android.app.ActivityManager; import android.os.BatteryConsumer; +import android.os.BatteryUsageStatsQuery; +import android.os.Process; import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; import android.view.Display; import androidx.test.filters.SmallTest; @@ -33,20 +37,37 @@ import org.junit.runner.RunWith; @SmallTest public class ScreenPowerCalculatorTest { private static final double PRECISION = 0.00001; + private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42; + private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 43; + private static final long MINUTE_IN_MS = 60 * 1000; + private static final long MINUTE_IN_US = 60 * 1000 * 1000; @Rule public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() - .setAveragePower(PowerProfile.POWER_SCREEN_ON, 360.0) - .setAveragePower(PowerProfile.POWER_SCREEN_FULL, 3600.0); + .setAveragePower(PowerProfile.POWER_SCREEN_ON, 36.0) + .setAveragePower(PowerProfile.POWER_SCREEN_FULL, 48.0); @Test - public void testTimerBasedModel() { - BatteryStatsImpl stats = mStatsRule.getBatteryStats(); + public void testEnergyBasedModel() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); - stats.noteScreenStateLocked(Display.STATE_ON, 1000, 1000, 1000); - stats.noteScreenBrightnessLocked(100, 1000, 1000); - stats.noteScreenBrightnessLocked(200, 2000, 2000); - stats.noteScreenStateLocked(Display.STATE_OFF, 3000, 3000, 3000); + batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0); + batteryStats.updateDisplayEnergyLocked(0, Display.STATE_ON, 2 * MINUTE_IN_MS); + + setFgState(APP_UID1, true, 2 * MINUTE_IN_MS, 2 * MINUTE_IN_MS); + setFgState(APP_UID1, false, 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS); + setFgState(APP_UID2, true, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + + batteryStats.updateDisplayEnergyLocked(300_000_000, Display.STATE_ON, + 60 * MINUTE_IN_MS); + + batteryStats.noteScreenStateLocked(Display.STATE_OFF, + 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS); + + batteryStats.updateDisplayEnergyLocked(100_000_000, Display.STATE_DOZE, + 120 * MINUTE_IN_MS); + + mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US); ScreenPowerCalculator calculator = new ScreenPowerCalculator(mStatsRule.getPowerProfile()); @@ -56,8 +77,79 @@ public class ScreenPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN); assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) - .isEqualTo(2000); + .isEqualTo(80 * MINUTE_IN_MS); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + .isWithin(PRECISION).of(30.03003); + + UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1); + assertThat(uid1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + .isEqualTo(18 * MINUTE_IN_MS); + assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) + .isWithin(PRECISION).of(8.44594); + + UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); + assertThat(uid2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + .isEqualTo(90 * MINUTE_IN_MS); + assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) + .isWithin(PRECISION).of(21.58408); + } + + @Test + public void testPowerProfileBasedModel() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0); + + setFgState(APP_UID1, true, 2 * MINUTE_IN_MS, 2 * MINUTE_IN_MS); + + batteryStats.noteScreenBrightnessLocked(100, 5 * MINUTE_IN_MS, 5 * MINUTE_IN_MS); + batteryStats.noteScreenBrightnessLocked(200, 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + + setFgState(APP_UID1, false, 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS); + + setFgState(APP_UID2, true, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + + batteryStats.noteScreenStateLocked(Display.STATE_OFF, + 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS); + + mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US); + + ScreenPowerCalculator calculator = + new ScreenPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); + + SystemBatteryConsumer consumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN); + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + .isEqualTo(80 * MINUTE_IN_MS); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) - .isWithin(PRECISION).of(1.2); + .isWithin(PRECISION).of(88.4); + + UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1); + assertThat(uid1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + .isEqualTo(18 * MINUTE_IN_MS); + assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) + .isWithin(PRECISION).of(14.73333); + + UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); + assertThat(uid2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + .isEqualTo(90 * MINUTE_IN_MS); + assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) + .isWithin(PRECISION).of(73.66666); + } + + private void setFgState(int uid, boolean fgOn, long realtimeMs, long uptimeMs) { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + if (fgOn) { + batteryStats.noteActivityResumedLocked(uid, realtimeMs, uptimeMs); + batteryStats.noteUidProcessStateLocked(uid, ActivityManager.PROCESS_STATE_TOP, + realtimeMs, uptimeMs); + } else { + batteryStats.noteActivityPausedLocked(uid, realtimeMs, uptimeMs); + batteryStats.noteUidProcessStateLocked(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY, + realtimeMs, uptimeMs); + } } } |