summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dmitri Plotnikov <dplotnikov@google.com> 2021-02-10 18:22:03 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2021-02-10 18:22:03 +0000
commite4d66ae2fe2f35635258c4a28c2bcba6924c355b (patch)
treef42c6f3e4b850018b076984c8545ce175f7a42b2
parent1c319ea03fe7c1ec5f64ec057bc51be2a6f6e9dc (diff)
parenta84c096ad965ad85e2f9a0f5d5ce1eef00163a31 (diff)
Merge "Make ScreenPowerCalculator use measured energy with BatteryUsageStats" into sc-dev
-rw-r--r--core/java/android/os/BatteryConsumer.java8
-rw-r--r--core/java/android/os/UidBatteryConsumer.java3
-rw-r--r--core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java2
-rw-r--r--core/java/com/android/internal/os/PowerCalculator.java6
-rw-r--r--core/java/com/android/internal/os/ScreenPowerCalculator.java172
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java6
-rw-r--r--core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java112
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);
+ }
}
}