Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3214255b authored by Michael Wachenschwanz's avatar Michael Wachenschwanz
Browse files

Utilize measured energy for multidisplay power attribution

Fixes: 194107383
Test: atest FrameworksCoreTests:BatteryStatsTests
Change-Id: I504d9c655382c2c6eb664e76ead88bce12da1ae4
parent e280aaca
Loading
Loading
Loading
Loading
+61 −15
Original line number Diff line number Diff line
@@ -903,6 +903,11 @@ public class BatteryStatsImpl extends BatteryStats {
         */
        public StopwatchTimer[] screenBrightnessTimers =
                new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
        /**
         * Per display screen state the last time {@link #updateDisplayMeasuredEnergyStatsLocked}
         * was called.
         */
        public int screenStateAtLastEnergyMeasurement = Display.STATE_UNKNOWN;
        DisplayBatteryStats(Clocks clocks, TimeBase timeBase) {
            screenOnTimer = new StopwatchTimer(clocks, null, -1, null,
@@ -929,6 +934,8 @@ public class BatteryStatsImpl extends BatteryStats {
    DisplayBatteryStats[] mPerDisplayBatteryStats;
    private int mDisplayMismatchWtfCount = 0;
    boolean mInteractive;
    StopwatchTimer mInteractiveTimer;
@@ -1073,8 +1080,6 @@ public class BatteryStatsImpl extends BatteryStats {
    @GuardedBy("this")
    @VisibleForTesting
    protected @Nullable MeasuredEnergyStats mGlobalMeasuredEnergyStats;
    /** Last known screen state. Needed for apportioning display energy. */
    int mScreenStateAtLastEnergyMeasurement = Display.STATE_UNKNOWN;
    /** Bluetooth Power calculator for attributing measured bluetooth charge consumption to uids */
    @Nullable BluetoothPowerCalculator mBluetoothPowerCalculator = null;
    /** Cpu Power calculator for attributing measured cpu charge consumption to uids */
@@ -12924,22 +12929,43 @@ public class BatteryStatsImpl extends BatteryStats {
     * is always 0 when the screen is not "ON" and whenever the rail energy is 0 (if supported).
     * To the extent that those assumptions are violated, the algorithm will err.
     *
     * @param chargeUC amount of charge (microcoulombs) used by Display since this was last called.
     * @param screenState screen state at the time this data collection was scheduled
     * @param chargesUC amount of charge (microcoulombs) used by each Display since this was last
     *                 called.
     * @param screenStates each screen state at the time this data collection was scheduled
     */
    @GuardedBy("this")
    public void updateDisplayMeasuredEnergyStatsLocked(long chargeUC, int screenState,
    public void updateDisplayMeasuredEnergyStatsLocked(long[] chargesUC, int[] screenStates,
            long elapsedRealtimeMs) {
        if (DEBUG_ENERGY) Slog.d(TAG, "Updating display stats: " + chargeUC);
        if (DEBUG_ENERGY) Slog.d(TAG, "Updating display stats: " + Arrays.toString(chargesUC));
        if (mGlobalMeasuredEnergyStats == null) {
            return;
        }
        final @StandardPowerBucket int powerBucket =
                MeasuredEnergyStats.getDisplayPowerBucket(mScreenStateAtLastEnergyMeasurement);
        mScreenStateAtLastEnergyMeasurement = screenState;
        final int numDisplays;
        if (mPerDisplayBatteryStats.length == screenStates.length) {
            numDisplays = screenStates.length;
        } else {
            // if this point is reached, it will be reached every display state change.
            // Rate limit the wtf logging to once every 100 display updates.
            if (mDisplayMismatchWtfCount++ % 100 == 0) {
                Slog.wtf(TAG, "Mismatch between PowerProfile reported display count ("
                        + mPerDisplayBatteryStats.length
                        + ") and PowerStatsHal reported display count (" + screenStates.length
                        + ")");
            }
            // Keep the show going, use the shorter of the two.
            numDisplays = mPerDisplayBatteryStats.length < screenStates.length
                    ? mPerDisplayBatteryStats.length : screenStates.length;
        }
        if (!mOnBatteryInternal || chargeUC <= 0) {
        final int[] oldScreenStates = new int[numDisplays];
        for (int i = 0; i < numDisplays; i++) {
            final int screenState = screenStates[i];
            oldScreenStates[i] = mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement;
            mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement = screenState;
        }
        if (!mOnBatteryInternal) {
            // There's nothing further to update.
            return;
        }
@@ -12954,17 +12980,31 @@ public class BatteryStatsImpl extends BatteryStats {
            return;
        }
        long totalScreenOnChargeUC = 0;
        for (int i = 0; i < numDisplays; i++) {
            final long chargeUC = chargesUC[i];
            if (chargeUC <= 0) {
                // There's nothing further to update.
                continue;
            }
            final @StandardPowerBucket int powerBucket =
                    MeasuredEnergyStats.getDisplayPowerBucket(oldScreenStates[i]);
            mGlobalMeasuredEnergyStats.updateStandardBucket(powerBucket, chargeUC);
            if (powerBucket == MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON) {
                totalScreenOnChargeUC += chargeUC;
            }
        }
        // Now we blame individual apps, but only if the display was ON.
        if (powerBucket != MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON) {
        if (totalScreenOnChargeUC <= 0) {
            return;
        }
        // TODO(b/175726779): Consider unifying the code with the non-rail display power blaming.
        // NOTE: fg time is NOT pooled. If two uids are both somehow in fg, then that time is
        // 'double counted' and will simply exceed the realtime that elapsed.
        // If multidisplay becomes a reality, this is probably more reasonable than pooling.
        // TODO(b/175726779): collect per display uid visibility for display power attribution.
        // Collect total time since mark so that we can normalize power.
        final SparseDoubleArray fgTimeUsArray = new SparseDoubleArray();
@@ -12977,7 +13017,8 @@ public class BatteryStatsImpl extends BatteryStats {
            if (fgTimeUs == 0) continue;
            fgTimeUsArray.put(uid.getUid(), (double) fgTimeUs);
        }
        distributeEnergyToUidsLocked(powerBucket, chargeUC, fgTimeUsArray, 0);
        distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON,
                totalScreenOnChargeUC, fgTimeUsArray, 0);
    }
    /**
@@ -14883,7 +14924,12 @@ public class BatteryStatsImpl extends BatteryStats {
    public void initMeasuredEnergyStatsLocked(@Nullable boolean[] supportedStandardBuckets,
            String[] customBucketNames) {
        boolean supportedBucketMismatch = false;
        mScreenStateAtLastEnergyMeasurement = mScreenState;
        final int numDisplays = mPerDisplayBatteryStats.length;
        for (int i = 0; i < numDisplays; i++) {
            final int screenState = mPerDisplayBatteryStats[i].screenState;
            mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement = screenState;
        }
        if (supportedStandardBuckets == null) {
            if (mGlobalMeasuredEnergyStats != null) {
+67 −5
Original line number Diff line number Diff line
@@ -46,19 +46,20 @@ public class AmbientDisplayPowerCalculatorTest {
        mStatsRule.initMeasuredEnergyStatsLocked();
        BatteryStatsImpl stats = mStatsRule.getBatteryStats();

        stats.updateDisplayMeasuredEnergyStatsLocked(300_000_000, Display.STATE_ON, 0);
        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300_000_000},
                new int[]{Display.STATE_ON}, 0);

        stats.noteScreenStateLocked(0, Display.STATE_DOZE, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS,
                30 * MINUTE_IN_MS);

        stats.updateDisplayMeasuredEnergyStatsLocked(200_000_000, Display.STATE_DOZE,
                30 * MINUTE_IN_MS);
        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{200_000_000},
                new int[]{Display.STATE_DOZE}, 30 * MINUTE_IN_MS);

        stats.noteScreenStateLocked(0, Display.STATE_OFF, 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS,
                120 * MINUTE_IN_MS);

        stats.updateDisplayMeasuredEnergyStatsLocked(100_000_000, Display.STATE_OFF,
                120 * MINUTE_IN_MS);
        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100_000_000},
                new int[]{Display.STATE_OFF}, 120 * MINUTE_IN_MS);

        AmbientDisplayPowerCalculator calculator =
                new AmbientDisplayPowerCalculator(mStatsRule.getPowerProfile());
@@ -75,6 +76,67 @@ public class AmbientDisplayPowerCalculatorTest {
                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
    }

    @Test
    public void testMeasuredEnergyBasedModel_multiDisplay() {
        mStatsRule.initMeasuredEnergyStatsLocked()
                .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 1, 20.0)
                .setNumDisplays(2);
        BatteryStatsImpl stats = mStatsRule.getBatteryStats();


        final int[] screenStates = new int[] {Display.STATE_OFF, Display.STATE_OFF};

        stats.noteScreenStateLocked(0, screenStates[0], 0, 0, 0);
        stats.noteScreenStateLocked(1, screenStates[1], 0, 0, 0);
        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300, 400}, screenStates, 0);

        // Switch display0 to doze
        screenStates[0] = Display.STATE_DOZE;
        stats.noteScreenStateLocked(0, screenStates[0], 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS,
                30 * MINUTE_IN_MS);
        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{200, 300},
                screenStates, 30 * MINUTE_IN_MS);

        // Switch display1 to doze
        screenStates[1] = Display.STATE_DOZE;
        stats.noteScreenStateLocked(1, Display.STATE_DOZE, 90 * MINUTE_IN_MS, 90 * MINUTE_IN_MS,
                90 * MINUTE_IN_MS);
        // 100,000,000 uC should be attributed to display 0 doze here.
        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100_000_000, 700_000_000},
                screenStates, 90 * MINUTE_IN_MS);

        // Switch display0 to off
        screenStates[0] = Display.STATE_OFF;
        stats.noteScreenStateLocked(0, screenStates[0], 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS,
                120 * MINUTE_IN_MS);
        // 40,000,000 and 70,000,000 uC should be attributed to display 0 and 1 doze here.
        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{40_000_000, 70_000_000},
                screenStates, 120 * MINUTE_IN_MS);

        // Switch display1 to off
        screenStates[1] = Display.STATE_OFF;
        stats.noteScreenStateLocked(1, screenStates[1], 150 * MINUTE_IN_MS, 150 * MINUTE_IN_MS,
                150 * MINUTE_IN_MS);
        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100, 90_000_000}, screenStates,
                150 * MINUTE_IN_MS);
        // 90,000,000 uC should be attributed to display 1 doze here.

        AmbientDisplayPowerCalculator calculator =
                new AmbientDisplayPowerCalculator(mStatsRule.getPowerProfile());

        mStatsRule.apply(calculator);

        BatteryConsumer consumer = mStatsRule.getDeviceBatteryConsumer();
        assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
                .isEqualTo(120 * MINUTE_IN_MS);
        // 100,000,000 + 40,000,000 + 70,000,000 + 90,000,000 uC / 1000 (micro-/milli-) / 3600
        // (seconds/hour) = 27.777778 mAh
        assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
                .isWithin(PRECISION).of(83.33333);
        assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
    }

    @Test
    public void testPowerProfileBasedModel() {
        BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+17 −17
Original line number Diff line number Diff line
@@ -998,7 +998,7 @@ public class BatteryStatsNoteTest extends TestCase {
        bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});

        clocks.realtime = 0;
        int screen = Display.STATE_OFF;
        int[] screen = new int[]{Display.STATE_OFF};
        boolean battery = false;

        final int uid1 = 10500;
@@ -1008,35 +1008,35 @@ public class BatteryStatsNoteTest extends TestCase {
        long globalDoze = 0;

        // Case A: uid1 off, uid2 off, battery off, screen off
        bi.updateTimeBasesLocked(battery, screen, clocks.realtime*1000, 0);
        bi.updateTimeBasesLocked(battery, screen[0], clocks.realtime * 1000, 0);
        bi.setOnBatteryInternal(battery);
        bi.updateDisplayMeasuredEnergyStatsLocked(500_000, screen, clocks.realtime);
        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{500_000}, screen, clocks.realtime);
        checkMeasuredCharge("A", uid1, blame1, uid2, blame2, globalDoze, bi);

        // Case B: uid1 off, uid2 off, battery ON,  screen off
        clocks.realtime += 17;
        battery = true;
        bi.updateTimeBasesLocked(battery, screen, clocks.realtime*1000, 0);
        bi.updateTimeBasesLocked(battery, screen[0], clocks.realtime * 1000, 0);
        bi.setOnBatteryInternal(battery);
        clocks.realtime += 19;
        bi.updateDisplayMeasuredEnergyStatsLocked(510_000, screen, clocks.realtime);
        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{510_000}, screen, clocks.realtime);
        checkMeasuredCharge("B", uid1, blame1, uid2, blame2, globalDoze, bi);

        // Case C: uid1 ON,  uid2 off, battery on,  screen off
        clocks.realtime += 18;
        setFgState(uid1, true, bi);
        clocks.realtime += 18;
        bi.updateDisplayMeasuredEnergyStatsLocked(520_000, screen, clocks.realtime);
        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{520_000}, screen, clocks.realtime);
        checkMeasuredCharge("C", uid1, blame1, uid2, blame2, globalDoze, bi);

        // Case D: uid1 on,  uid2 off, battery on,  screen ON
        clocks.realtime += 17;
        screen = Display.STATE_ON;
        bi.updateDisplayMeasuredEnergyStatsLocked(521_000, screen, clocks.realtime);
        screen[0] = Display.STATE_ON;
        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{521_000}, screen, clocks.realtime);
        blame1 += 0; // Screen had been off during the measurement period
        checkMeasuredCharge("D.1", uid1, blame1, uid2, blame2, globalDoze, bi);
        clocks.realtime += 101;
        bi.updateDisplayMeasuredEnergyStatsLocked(530_000, screen, clocks.realtime);
        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{530_000}, screen, clocks.realtime);
        blame1 += 530_000;
        checkMeasuredCharge("D.2", uid1, blame1, uid2, blame2, globalDoze, bi);

@@ -1044,7 +1044,7 @@ public class BatteryStatsNoteTest extends TestCase {
        clocks.realtime += 20;
        setFgState(uid2, true, bi);
        clocks.realtime += 40;
        bi.updateDisplayMeasuredEnergyStatsLocked(540_000, screen, clocks.realtime);
        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{540_000}, screen, clocks.realtime);
        // In the past 60ms, sum of fg is 20+40+40=100ms. uid1 is blamed for 60/100; uid2 for 40/100
        blame1 += 540_000 * (20 + 40) / (20 + 40 + 40);
        blame2 += 540_000 * (0 + 40) / (20 + 40 + 40);
@@ -1054,7 +1054,7 @@ public class BatteryStatsNoteTest extends TestCase {
        clocks.realtime += 40;
        setFgState(uid2, false, bi);
        clocks.realtime += 120;
        bi.updateDisplayMeasuredEnergyStatsLocked(550_000, screen, clocks.realtime);
        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{550_000}, screen, clocks.realtime);
        // In the past 160ms, sum f fg is 200ms. uid1 is blamed for 40+120 of it; uid2 for 40 of it.
        blame1 += 550_000 * (40 + 120) / (40 + 40 + 120);
        blame2 += 550_000 * (40 + 0) / (40 + 40 + 120);
@@ -1062,15 +1062,15 @@ public class BatteryStatsNoteTest extends TestCase {

        // Case G: uid1 on,  uid2 off,  battery on, screen DOZE
        clocks.realtime += 5;
        screen = Display.STATE_DOZE;
        bi.updateDisplayMeasuredEnergyStatsLocked(570_000, screen, clocks.realtime);
        screen[0] = Display.STATE_DOZE;
        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{570_000}, screen, clocks.realtime);
        blame1 += 570_000; // All of this pre-doze time is blamed on uid1.
        checkMeasuredCharge("G", uid1, blame1, uid2, blame2, globalDoze, bi);

        // Case H: uid1 on,  uid2 off,  battery on, screen ON
        clocks.realtime += 6;
        screen = Display.STATE_ON;
        bi.updateDisplayMeasuredEnergyStatsLocked(580_000, screen, clocks.realtime);
        screen[0] = Display.STATE_ON;
        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{580_000}, screen, clocks.realtime);
        blame1 += 0; // The screen had been doze during the energy period
        globalDoze += 580_000;
        checkMeasuredCharge("H", uid1, blame1, uid2, blame2, globalDoze, bi);
+106 −7
Original line number Diff line number Diff line
@@ -56,12 +56,13 @@ public class ScreenPowerCalculatorTest {
        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();

        batteryStats.noteScreenStateLocked(0, Display.STATE_ON, 0, 0, 0);
        batteryStats.updateDisplayMeasuredEnergyStatsLocked(0, Display.STATE_ON, 0);
        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{0},
                new int[]{Display.STATE_ON}, 0);
        setProcState(APP_UID1, ActivityManager.PROCESS_STATE_TOP, true,
                0, 0);

        batteryStats.updateDisplayMeasuredEnergyStatsLocked(200_000_000, Display.STATE_ON,
                15 * MINUTE_IN_MS);
        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{200_000_000},
                new int[]{Display.STATE_ON}, 15 * MINUTE_IN_MS);

        setProcState(APP_UID1, ActivityManager.PROCESS_STATE_CACHED_EMPTY, false,
                20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
@@ -69,16 +70,16 @@ public class ScreenPowerCalculatorTest {
        setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP, true,
                20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);

        batteryStats.updateDisplayMeasuredEnergyStatsLocked(300_000_000, Display.STATE_ON,
                60 * MINUTE_IN_MS);
        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300_000_000},
                new int[]{Display.STATE_ON}, 60 * MINUTE_IN_MS);

        batteryStats.noteScreenStateLocked(0, Display.STATE_OFF,
                80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
        setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP_SLEEPING, false,
                80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);

        batteryStats.updateDisplayMeasuredEnergyStatsLocked(100_000_000, Display.STATE_DOZE,
                120 * MINUTE_IN_MS);
        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100_000_000},
                new int[]{Display.STATE_DOZE}, 120 * MINUTE_IN_MS);

        mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US);

@@ -131,6 +132,104 @@ public class ScreenPowerCalculatorTest {
                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
    }


    @Test
    public void testMeasuredEnergyBasedModel_multiDisplay() {
        mStatsRule.initMeasuredEnergyStatsLocked()
                .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 1, 60.0)
                .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 1, 100.0)
                .setNumDisplays(2);

        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();

        final int[] screenStates = new int[]{Display.STATE_ON, Display.STATE_OFF};

        batteryStats.noteScreenStateLocked(0, screenStates[0], 0, 0, 0);
        batteryStats.noteScreenStateLocked(1, screenStates[1], 0, 0, 0);
        batteryStats.noteScreenBrightnessLocked(0, 255, 0, 0);
        setProcState(APP_UID1, ActivityManager.PROCESS_STATE_TOP, true, 0, 0);
        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300, 400}, screenStates, 0);

        batteryStats.noteScreenBrightnessLocked(0, 100, 5 * MINUTE_IN_MS, 5 * MINUTE_IN_MS);
        batteryStats.noteScreenBrightnessLocked(0, 200, 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);

        setProcState(APP_UID1, ActivityManager.PROCESS_STATE_CACHED_EMPTY, false,
                20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
        setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP, true,
                20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);

        screenStates[0] = Display.STATE_OFF;
        screenStates[1] = Display.STATE_ON;
        batteryStats.noteScreenStateLocked(0, screenStates[0],
                80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
        batteryStats.noteScreenStateLocked(1, screenStates[1], 80 * MINUTE_IN_MS,
                80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{600_000_000, 500},
                screenStates, 80 * MINUTE_IN_MS);

        batteryStats.noteScreenBrightnessLocked(1, 25, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
        batteryStats.noteScreenBrightnessLocked(1, 250, 86 * MINUTE_IN_MS, 86 * MINUTE_IN_MS);
        batteryStats.noteScreenBrightnessLocked(1, 75, 98 * MINUTE_IN_MS, 98 * MINUTE_IN_MS);

        screenStates[1] = Display.STATE_OFF;
        batteryStats.noteScreenStateLocked(1, screenStates[1], 110 * MINUTE_IN_MS,
                110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS);
        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{700, 800_000_000},
                screenStates, 110 * MINUTE_IN_MS);

        setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP_SLEEPING, false,
                110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS);

        mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US);

        ScreenPowerCalculator calculator =
                new ScreenPowerCalculator(mStatsRule.getPowerProfile());

        mStatsRule.apply(calculator);

        BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
        assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isEqualTo(110 * MINUTE_IN_MS);
        // (600000000 + 800000000) uAs * (1 mA / 1000 uA) * (1 h / 3600 s)  = 166.66666 mAh
        assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isWithin(PRECISION).of(388.88888);
        assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);

        UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
        assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isEqualTo(20 * MINUTE_IN_MS);

        // Uid1 ran for 20 out of 80 min during the first Display update.
        // It also ran for 5 out of 45 min during the second Display update:
        // Uid1 charge = 20 / 80 * 600000000 mAs = 41.66666 mAh
        assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isWithin(PRECISION).of(41.66666);
        assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);

        UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
        assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isEqualTo(90 * MINUTE_IN_MS);

        // Uid2 ran for 60 out of 80 min during the first Display update.
        // It also ran for all of the second Display update:
        // Uid1 charge = 60 / 80 * 600000000 + 800000000 mAs = 347.22222 mAh
        assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isWithin(PRECISION).of(347.22222);
        assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);

        BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
        assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isEqualTo(110 * MINUTE_IN_MS);
        assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isWithin(PRECISION).of(388.88888);
        assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);

    }

    @Test
    public void testPowerProfileBasedModel() {
        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+4 −6
Original line number Diff line number Diff line
@@ -669,12 +669,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
            if (measuredEnergyDeltas != null) {
                final long[] displayChargeUC = measuredEnergyDeltas.displayChargeUC;
                if (displayChargeUC != null && displayChargeUC.length > 0) {
                    // TODO (b/194107383): pass all display ordinals to mStats with
                    //  displayScreenStates
                    final long primaryDisplayChargeUC = displayChargeUC[0];
                    // If updating, pass in what BatteryExternalStatsWorker thinks screenState is.
                    mStats.updateDisplayMeasuredEnergyStatsLocked(primaryDisplayChargeUC,
                            screenState, elapsedRealtime);
                    // If updating, pass in what BatteryExternalStatsWorker thinks
                    // displayScreenStates is.
                    mStats.updateDisplayMeasuredEnergyStatsLocked(displayChargeUC,
                            displayScreenStates, elapsedRealtime);
                }

                final long gnssChargeUC = measuredEnergyDeltas.gnssChargeUC;