diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index fb9911811da92..79ec55482c500 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -2302,6 +2302,38 @@ public abstract class BatteryStats implements Parcelable { */ public abstract Timer getScreenBrightnessTimer(int brightnessBin); + /** + * Returns the number of physical displays on the device. + * + * {@hide} + */ + public abstract int getDisplayCount(); + + /** + * Returns the time in microseconds that the screen has been on for a display while the + * device was running on battery. + * + * {@hide} + */ + public abstract long getDisplayScreenOnTime(int display, long elapsedRealtimeUs); + + /** + * Returns the time in microseconds that a display has been dozing while the device was + * running on battery. + * + * {@hide} + */ + public abstract long getDisplayScreenDozeTime(int display, long elapsedRealtimeUs); + + /** + * Returns the time in microseconds that a display has been on with the given brightness + * level while the device was running on battery. + * + * {@hide} + */ + public abstract long getDisplayScreenBrightnessTime(int display, int brightnessBin, + long elapsedRealtimeUs); + /** * Returns the time in microseconds that power save mode has been enabled while the device was * running on battery. @@ -5038,6 +5070,71 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } + final int numDisplays = getDisplayCount(); + if (numDisplays > 1) { + pw.println(""); + pw.print(prefix); + sb.setLength(0); + sb.append(prefix); + sb.append(" MULTI-DISPLAY POWER SUMMARY START"); + pw.println(sb.toString()); + + for (int display = 0; display < numDisplays; display++) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Display "); + sb.append(display); + sb.append(" Statistics:"); + pw.println(sb.toString()); + + final long displayScreenOnTime = getDisplayScreenOnTime(display, rawRealtime); + sb.setLength(0); + sb.append(prefix); + sb.append(" Screen on: "); + formatTimeMs(sb, displayScreenOnTime / 1000); + sb.append("("); + sb.append(formatRatioLocked(displayScreenOnTime, whichBatteryRealtime)); + sb.append(") "); + pw.println(sb.toString()); + + sb.setLength(0); + sb.append(" Screen brightness levels:"); + didOne = false; + for (int bin = 0; bin < NUM_SCREEN_BRIGHTNESS_BINS; bin++) { + final long timeUs = getDisplayScreenBrightnessTime(display, bin, rawRealtime); + if (timeUs == 0) { + continue; + } + didOne = true; + sb.append("\n "); + sb.append(prefix); + sb.append(SCREEN_BRIGHTNESS_NAMES[bin]); + sb.append(" "); + formatTimeMs(sb, timeUs / 1000); + sb.append("("); + sb.append(formatRatioLocked(timeUs, displayScreenOnTime)); + sb.append(")"); + } + if (!didOne) sb.append(" (no activity)"); + pw.println(sb.toString()); + + final long displayScreenDozeTimeUs = getDisplayScreenDozeTime(display, rawRealtime); + sb.setLength(0); + sb.append(prefix); + sb.append(" Screen Doze: "); + formatTimeMs(sb, displayScreenDozeTimeUs / 1000); + sb.append("("); + sb.append(formatRatioLocked(displayScreenDozeTimeUs, whichBatteryRealtime)); + sb.append(") "); + pw.println(sb.toString()); + } + pw.print(prefix); + sb.setLength(0); + sb.append(prefix); + sb.append(" MULTI-DISPLAY POWER SUMMARY END"); + pw.println(sb.toString()); + } + pw.println(""); pw.print(prefix); sb.setLength(0); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index a817119a735f9..17fdb2e7054f5 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -690,7 +690,7 @@ public class BatteryStatsImpl extends BatteryStats { * Schedule a sync because of a screen state change. */ Future scheduleSyncDueToScreenStateChange(int flags, boolean onBattery, - boolean onBatteryScreenOff, int screenState); + boolean onBatteryScreenOff, int screenState, int[] perDisplayScreenStates); Future scheduleCpuSyncDueToWakelockChange(long delayMillis); void cancelCpuSyncDueToWakelockChange(); Future scheduleSyncDueToBatteryLevelChange(long delayMillis); @@ -851,17 +851,84 @@ public class BatteryStatsImpl extends BatteryStats { public boolean mRecordAllHistory; boolean mNoAutoReset; + /** + * Overall screen state. For multidisplay devices, this represents the current highest screen + * state of the displays. + */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected int mScreenState = Display.STATE_UNKNOWN; + /** + * Overall screen on timer. For multidisplay devices, this represents the time spent with at + * least one display in the screen on state. + */ StopwatchTimer mScreenOnTimer; + /** + * Overall screen doze timer. For multidisplay devices, this represents the time spent with + * screen doze being the highest screen state. + */ StopwatchTimer mScreenDozeTimer; - + /** + * Overall screen brightness bin. For multidisplay devices, this represents the current + * brightest screen. + */ int mScreenBrightnessBin = -1; + /** + * Overall screen brightness timers. For multidisplay devices, the {@link mScreenBrightnessBin} + * timer will be active at any given time + */ final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS]; boolean mPretendScreenOff; + private static class DisplayBatteryStats { + /** + * Per display screen state. + */ + public int screenState = Display.STATE_UNKNOWN; + /** + * Per display screen on timers. + */ + public StopwatchTimer screenOnTimer; + /** + * Per display screen doze timers. + */ + public StopwatchTimer screenDozeTimer; + /** + * Per display screen brightness bins. + */ + public int screenBrightnessBin = -1; + /** + * Per display screen brightness timers. + */ + public StopwatchTimer[] screenBrightnessTimers = + new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS]; + + DisplayBatteryStats(Clocks clocks, TimeBase timeBase) { + screenOnTimer = new StopwatchTimer(clocks, null, -1, null, + timeBase); + screenDozeTimer = new StopwatchTimer(clocks, null, -1, null, + timeBase); + for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; i++) { + screenBrightnessTimers[i] = new StopwatchTimer(clocks, null, -100 - i, null, + timeBase); + } + } + + /** + * Reset display timers. + */ + public void reset(long elapsedRealtimeUs) { + screenOnTimer.reset(false, elapsedRealtimeUs); + screenDozeTimer.reset(false, elapsedRealtimeUs); + for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; i++) { + screenBrightnessTimers[i].reset(false, elapsedRealtimeUs); + } + } + } + + DisplayBatteryStats[] mPerDisplayBatteryStats; + boolean mInteractive; StopwatchTimer mInteractiveTimer; @@ -4308,8 +4375,10 @@ public class BatteryStatsImpl extends BatteryStats { public void setPretendScreenOff(boolean pretendScreenOff) { if (mPretendScreenOff != pretendScreenOff) { mPretendScreenOff = pretendScreenOff; - noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON, - mClocks.elapsedRealtime(), mClocks.uptimeMillis(), mClocks.currentTimeMillis()); + final int primaryScreenState = mPerDisplayBatteryStats[0].screenState; + noteScreenStateLocked(0, primaryScreenState, + mClocks.elapsedRealtime(), mClocks.uptimeMillis(), + mClocks.currentTimeMillis()); } } @@ -4907,29 +4976,158 @@ public class BatteryStatsImpl extends BatteryStats { } @GuardedBy("this") - public void noteScreenStateLocked(int state) { - noteScreenStateLocked(state, mClocks.elapsedRealtime(), mClocks.uptimeMillis(), + public void noteScreenStateLocked(int display, int state) { + noteScreenStateLocked(display, state, mClocks.elapsedRealtime(), mClocks.uptimeMillis(), mClocks.currentTimeMillis()); } @GuardedBy("this") - public void noteScreenStateLocked(int state, + public void noteScreenStateLocked(int display, int displayState, long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) { - state = mPretendScreenOff ? Display.STATE_OFF : state; - // Battery stats relies on there being 4 states. To accommodate this, new states beyond the // original 4 are mapped to one of the originals. - if (state > MAX_TRACKED_SCREEN_STATE) { - switch (state) { - case Display.STATE_VR: - state = Display.STATE_ON; + if (displayState > MAX_TRACKED_SCREEN_STATE) { + if (Display.isOnState(displayState)) { + displayState = Display.STATE_ON; + } else if (Display.isDozeState(displayState)) { + if (Display.isSuspendedState(displayState)) { + displayState = Display.STATE_DOZE_SUSPEND; + } else { + displayState = Display.STATE_DOZE; + } + } else if (Display.isOffState(displayState)) { + displayState = Display.STATE_OFF; + } else { + Slog.wtf(TAG, "Unknown screen state (not mapped): " + displayState); + displayState = Display.STATE_UNKNOWN; + } + } + // As of this point, displayState should be mapped to one of: + // - Display.STATE_ON, + // - Display.STATE_DOZE + // - Display.STATE_DOZE_SUSPEND + // - Display.STATE_OFF + // - Display.STATE_UNKNOWN + + int state; + int overallBin = mScreenBrightnessBin; + int externalUpdateFlag = 0; + boolean shouldScheduleSync = false; + final int numDisplay = mPerDisplayBatteryStats.length; + if (display < 0 || display >= numDisplay) { + Slog.wtf(TAG, "Unexpected note screen state for display " + display + " (only " + + mPerDisplayBatteryStats.length + " displays exist...)"); + return; + } + final DisplayBatteryStats displayStats = mPerDisplayBatteryStats[display]; + final int oldDisplayState = displayStats.screenState; + + if (oldDisplayState == displayState) { + // Nothing changed + state = mScreenState; + } else { + displayStats.screenState = displayState; + + // Stop timer for previous display state. + switch (oldDisplayState) { + case Display.STATE_ON: + displayStats.screenOnTimer.stopRunningLocked(elapsedRealtimeMs); + final int bin = displayStats.screenBrightnessBin; + if (bin >= 0) { + displayStats.screenBrightnessTimers[bin].stopRunningLocked( + elapsedRealtimeMs); + } + overallBin = evaluateOverallScreenBrightnessBinLocked(); + shouldScheduleSync = true; + break; + case Display.STATE_DOZE: + // Transition from doze to doze suspend can be ignored. + if (displayState == Display.STATE_DOZE_SUSPEND) break; + displayStats.screenDozeTimer.stopRunningLocked(elapsedRealtimeMs); + shouldScheduleSync = true; + break; + case Display.STATE_DOZE_SUSPEND: + // Transition from doze suspend to doze can be ignored. + if (displayState == Display.STATE_DOZE) break; + displayStats.screenDozeTimer.stopRunningLocked(elapsedRealtimeMs); + shouldScheduleSync = true; + break; + case Display.STATE_OFF: // fallthrough + case Display.STATE_UNKNOWN: + // Not tracked by timers. break; default: - Slog.wtf(TAG, "Unknown screen state (not mapped): " + state); + Slog.wtf(TAG, + "Attempted to stop timer for unexpected display state " + display); + } + + // Start timer for new display state. + switch (displayState) { + case Display.STATE_ON: + displayStats.screenOnTimer.startRunningLocked(elapsedRealtimeMs); + final int bin = displayStats.screenBrightnessBin; + if (bin >= 0) { + displayStats.screenBrightnessTimers[bin].startRunningLocked( + elapsedRealtimeMs); + } + overallBin = evaluateOverallScreenBrightnessBinLocked(); + shouldScheduleSync = true; break; + case Display.STATE_DOZE: + // Transition from doze suspend to doze can be ignored. + if (oldDisplayState == Display.STATE_DOZE_SUSPEND) break; + displayStats.screenDozeTimer.startRunningLocked(elapsedRealtimeMs); + shouldScheduleSync = true; + break; + case Display.STATE_DOZE_SUSPEND: + // Transition from doze to doze suspend can be ignored. + if (oldDisplayState == Display.STATE_DOZE) break; + displayStats.screenDozeTimer.startRunningLocked(elapsedRealtimeMs); + shouldScheduleSync = true; + break; + case Display.STATE_OFF: // fallthrough + case Display.STATE_UNKNOWN: + // Not tracked by timers. + break; + default: + Slog.wtf(TAG, + "Attempted to start timer for unexpected display state " + displayState + + " for display " + display); + } + + if (shouldScheduleSync + && mGlobalMeasuredEnergyStats != null + && mGlobalMeasuredEnergyStats.isStandardBucketSupported( + MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON)) { + // Display measured energy stats is available. Prepare to schedule an + // external sync. + externalUpdateFlag |= ExternalStatsSync.UPDATE_DISPLAY; + } + + // Reevaluate most important display screen state. + state = Display.STATE_UNKNOWN; + for (int i = 0; i < numDisplay; i++) { + final int tempState = mPerDisplayBatteryStats[i].screenState; + if (tempState == Display.STATE_ON + || state == Display.STATE_ON) { + state = Display.STATE_ON; + } else if (tempState == Display.STATE_DOZE + || state == Display.STATE_DOZE) { + state = Display.STATE_DOZE; + } else if (tempState == Display.STATE_DOZE_SUSPEND + || state == Display.STATE_DOZE_SUSPEND) { + state = Display.STATE_DOZE_SUSPEND; + } else if (tempState == Display.STATE_OFF + || state == Display.STATE_OFF) { + state = Display.STATE_OFF; + } } } + final boolean batteryRunning = mOnBatteryTimeBase.isRunning(); + final boolean batteryScreenOffRunning = mOnBatteryScreenOffTimeBase.isRunning(); + + state = mPretendScreenOff ? Display.STATE_OFF : state; if (mScreenState != state) { recordDailyStatsIfNeededLocked(true, currentTimeMs); final int oldState = mScreenState; @@ -4983,11 +5181,11 @@ public class BatteryStatsImpl extends BatteryStats { + Display.stateToString(state)); addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); } - // TODO: (Probably overkill) Have mGlobalMeasuredEnergyStats store supported flags and - // only update DISPLAY if it is. Currently overkill since CPU is scheduled anyway. - final int updateFlag = ExternalStatsSync.UPDATE_CPU | ExternalStatsSync.UPDATE_DISPLAY; - mExternalSync.scheduleSyncDueToScreenStateChange(updateFlag, - mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning(), state); + + // Per screen state Cpu stats needed. Prepare to schedule an external sync. + externalUpdateFlag |= ExternalStatsSync.UPDATE_CPU; + shouldScheduleSync = true; + if (Display.isOnState(state)) { updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state, uptimeMs * 1000, elapsedRealtimeMs * 1000); @@ -5005,33 +5203,116 @@ public class BatteryStatsImpl extends BatteryStats { updateDischargeScreenLevelsLocked(oldState, state); } } + + // Changing display states might have changed the screen used to determine the overall + // brightness. + maybeUpdateOverallScreenBrightness(overallBin, elapsedRealtimeMs, uptimeMs); + + if (shouldScheduleSync) { + final int numDisplays = mPerDisplayBatteryStats.length; + final int[] displayStates = new int[numDisplays]; + for (int i = 0; i < numDisplays; i++) { + displayStates[i] = mPerDisplayBatteryStats[i].screenState; + } + mExternalSync.scheduleSyncDueToScreenStateChange(externalUpdateFlag, + batteryRunning, batteryScreenOffRunning, state, displayStates); + } } @UnsupportedAppUsage public void noteScreenBrightnessLocked(int brightness) { - noteScreenBrightnessLocked(brightness, mClocks.elapsedRealtime(), mClocks.uptimeMillis()); + noteScreenBrightnessLocked(0, brightness); } - public void noteScreenBrightnessLocked(int brightness, long elapsedRealtimeMs, long uptimeMs) { + /** + * Note screen brightness change for a display. + */ + public void noteScreenBrightnessLocked(int display, int brightness) { + noteScreenBrightnessLocked(display, brightness, mClocks.elapsedRealtime(), + mClocks.uptimeMillis()); + } + + + /** + * Note screen brightness change for a display. + */ + public void noteScreenBrightnessLocked(int display, int brightness, long elapsedRealtimeMs, + long uptimeMs) { // Bin the brightness. int bin = brightness / (256/NUM_SCREEN_BRIGHTNESS_BINS); if (bin < 0) bin = 0; else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1; - if (mScreenBrightnessBin != bin) { - mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_BRIGHTNESS_MASK) - | (bin << HistoryItem.STATE_BRIGHTNESS_SHIFT); - if (DEBUG_HISTORY) Slog.v(TAG, "Screen brightness " + bin + " to: " - + Integer.toHexString(mHistoryCur.states)); - addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); + + final int overallBin; + + final int numDisplays = mPerDisplayBatteryStats.length; + if (display < 0 || display >= numDisplays) { + Slog.wtf(TAG, "Unexpected note screen brightness for display " + display + " (only " + + mPerDisplayBatteryStats.length + " displays exist...)"); + return; + } + + final DisplayBatteryStats displayStats = mPerDisplayBatteryStats[display]; + final int oldBin = displayStats.screenBrightnessBin; + if (oldBin == bin) { + // Nothing changed + overallBin = mScreenBrightnessBin; + } else { + displayStats.screenBrightnessBin = bin; + if (displayStats.screenState == Display.STATE_ON) { + if (oldBin >= 0) { + displayStats.screenBrightnessTimers[oldBin].stopRunningLocked( + elapsedRealtimeMs); + } + displayStats.screenBrightnessTimers[bin].startRunningLocked( + elapsedRealtimeMs); + } + overallBin = evaluateOverallScreenBrightnessBinLocked(); + } + + maybeUpdateOverallScreenBrightness(overallBin, elapsedRealtimeMs, uptimeMs); + } + + private int evaluateOverallScreenBrightnessBinLocked() { + int overallBin = -1; + final int numDisplays = getDisplayCount(); + for (int display = 0; display < numDisplays; display++) { + final int displayBrightnessBin; + if (mPerDisplayBatteryStats[display].screenState == Display.STATE_ON) { + displayBrightnessBin = mPerDisplayBatteryStats[display].screenBrightnessBin; + } else { + displayBrightnessBin = -1; + } + if (displayBrightnessBin > overallBin) { + overallBin = displayBrightnessBin; + } + } + return overallBin; + } + + private void maybeUpdateOverallScreenBrightness(int overallBin, long elapsedRealtimeMs, + long uptimeMs) { + if (mScreenBrightnessBin != overallBin) { + if (overallBin >= 0) { + mHistoryCur.states = (mHistoryCur.states & ~HistoryItem.STATE_BRIGHTNESS_MASK) + | (overallBin << HistoryItem.STATE_BRIGHTNESS_SHIFT); + if (DEBUG_HISTORY) { + Slog.v(TAG, "Screen brightness " + overallBin + " to: " + + Integer.toHexString(mHistoryCur.states)); + } + addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); + } if (mScreenState == Display.STATE_ON) { if (mScreenBrightnessBin >= 0) { mScreenBrightnessTimer[mScreenBrightnessBin] .stopRunningLocked(elapsedRealtimeMs); } - mScreenBrightnessTimer[bin] - .startRunningLocked(elapsedRealtimeMs); + if (overallBin >= 0) { + mScreenBrightnessTimer[overallBin] + .startRunningLocked(elapsedRealtimeMs); + } } - mScreenBrightnessBin = bin; + mScreenBrightnessBin = overallBin; } } @@ -6693,6 +6974,31 @@ public class BatteryStatsImpl extends BatteryStats { return mScreenBrightnessTimer[brightnessBin]; } + @Override + public int getDisplayCount() { + return mPerDisplayBatteryStats.length; + } + + @Override + public long getDisplayScreenOnTime(int display, long elapsedRealtimeUs) { + return mPerDisplayBatteryStats[display].screenOnTimer.getTotalTimeLocked(elapsedRealtimeUs, + STATS_SINCE_CHARGED); + } + + @Override + public long getDisplayScreenDozeTime(int display, long elapsedRealtimeUs) { + return mPerDisplayBatteryStats[display].screenDozeTimer.getTotalTimeLocked( + elapsedRealtimeUs, STATS_SINCE_CHARGED); + } + + @Override + public long getDisplayScreenBrightnessTime(int display, int brightnessBin, + long elapsedRealtimeUs) { + final DisplayBatteryStats displayStats = mPerDisplayBatteryStats[display]; + return displayStats.screenBrightnessTimers[brightnessBin].getTotalTimeLocked( + elapsedRealtimeUs, STATS_SINCE_CHARGED); + } + @Override public long getInteractiveTime(long elapsedRealtimeUs, int which) { return mInteractiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } @@ -10694,6 +11000,10 @@ public class BatteryStatsImpl extends BatteryStats { mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null, mOnBatteryTimeBase); } + + mPerDisplayBatteryStats = new DisplayBatteryStats[1]; + mPerDisplayBatteryStats[0] = new DisplayBatteryStats(mClocks, mOnBatteryTimeBase); + mInteractiveTimer = new StopwatchTimer(mClocks, null, -10, null, mOnBatteryTimeBase); mPowerSaveModeEnabledTimer = new StopwatchTimer(mClocks, null, -2, null, mOnBatteryTimeBase); @@ -10806,6 +11116,8 @@ public class BatteryStatsImpl extends BatteryStats { // Initialize the estimated battery capacity to a known preset one. mEstimatedBatteryCapacityMah = (int) mPowerProfile.getBatteryCapacity(); } + + setDisplayCountLocked(mPowerProfile.getNumDisplays()); } PowerProfile getPowerProfile() { @@ -10838,6 +11150,16 @@ public class BatteryStatsImpl extends BatteryStats { mExternalSync = sync; } + /** + * Initialize and set multi display timers and states. + */ + public void setDisplayCountLocked(int numDisplays) { + mPerDisplayBatteryStats = new DisplayBatteryStats[numDisplays]; + for (int i = 0; i < numDisplays; i++) { + mPerDisplayBatteryStats[i] = new DisplayBatteryStats(mClocks, mOnBatteryTimeBase); + } + } + public void updateDailyDeadlineLocked() { // Get the current time. long currentTimeMs = mDailyStartTimeMs = mClocks.currentTimeMillis(); @@ -11314,6 +11636,11 @@ public class BatteryStatsImpl extends BatteryStats { mScreenBrightnessTimer[i].reset(false, elapsedRealtimeUs); } + final int numDisplays = mPerDisplayBatteryStats.length; + for (int i = 0; i < numDisplays; i++) { + mPerDisplayBatteryStats[i].reset(elapsedRealtimeUs); + } + if (mPowerProfile != null) { mEstimatedBatteryCapacityMah = (int) mPowerProfile.getBatteryCapacity(); } else { diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java index d76037eb1cab9..e95f6c2946339 100644 --- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java @@ -47,13 +47,13 @@ public class AmbientDisplayPowerCalculatorTest { stats.updateDisplayMeasuredEnergyStatsLocked(300_000_000, Display.STATE_ON, 0); - stats.noteScreenStateLocked(Display.STATE_DOZE, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS, + 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.noteScreenStateLocked(Display.STATE_OFF, 120 * MINUTE_IN_MS, 120 * 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, @@ -78,9 +78,9 @@ public class AmbientDisplayPowerCalculatorTest { public void testPowerProfileBasedModel() { BatteryStatsImpl stats = mStatsRule.getBatteryStats(); - stats.noteScreenStateLocked(Display.STATE_DOZE, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS, + stats.noteScreenStateLocked(0, Display.STATE_DOZE, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); - stats.noteScreenStateLocked(Display.STATE_OFF, 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS, + stats.noteScreenStateLocked(0, Display.STATE_OFF, 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS); AmbientDisplayPowerCalculator calculator = diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java index d4799a8f5fd38..4adc09d72e94c 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java @@ -16,9 +16,13 @@ package com.android.internal.os; +import static android.os.BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; import static android.os.BatteryStats.STATS_SINCE_CHARGED; import static android.os.BatteryStats.WAKE_TYPE_PARTIAL; +import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU; +import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY; + import android.app.ActivityManager; import android.os.BatteryStats; import android.os.BatteryStats.HistoryItem; @@ -37,8 +41,10 @@ import com.android.internal.power.MeasuredEnergyStats; import junit.framework.TestCase; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.function.IntConsumer; /** * Test various BatteryStatsImpl noteStart methods. @@ -317,18 +323,130 @@ public class BatteryStatsNoteTest extends TestCase { public void testNoteScreenStateLocked() throws Exception { final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"}); bi.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); - bi.noteScreenStateLocked(Display.STATE_ON); - bi.noteScreenStateLocked(Display.STATE_DOZE); + bi.noteScreenStateLocked(0, Display.STATE_ON); + + bi.noteScreenStateLocked(0, Display.STATE_DOZE); assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning()); - assertEquals(bi.getScreenState(), Display.STATE_DOZE); - bi.noteScreenStateLocked(Display.STATE_ON); + assertEquals(Display.STATE_DOZE, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_ON); assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning()); - assertEquals(bi.getScreenState(), Display.STATE_ON); - bi.noteScreenStateLocked(Display.STATE_OFF); + assertEquals(Display.STATE_ON, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_OFF); assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning()); - assertEquals(bi.getScreenState(), Display.STATE_OFF); + assertEquals(Display.STATE_OFF, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_DOZE_SUSPEND); + assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_DOZE_SUSPEND, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + // STATE_VR note should map to STATE_ON. + bi.noteScreenStateLocked(0, Display.STATE_VR); + assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_ON, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + // STATE_ON_SUSPEND note should map to STATE_ON. + bi.noteScreenStateLocked(0, Display.STATE_ON_SUSPEND); + assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_ON, bi.getScreenState()); + // Transition from ON to ON state should not cause an External Sync + assertEquals(0, bi.getAndClearExternalStatsSyncFlags()); + } + + /** + * Test BatteryStatsImpl.noteScreenStateLocked sets timebases and screen states correctly for + * multi display devices + */ + @SmallTest + public void testNoteScreenStateLocked_multiDisplay() throws Exception { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.setDisplayCountLocked(2); + bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"}); + + bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); + bi.noteScreenStateLocked(0, Display.STATE_OFF); + bi.noteScreenStateLocked(1, Display.STATE_OFF); + + bi.noteScreenStateLocked(0, Display.STATE_DOZE); + assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_DOZE, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_ON); + assertEquals(Display.STATE_ON, bi.getScreenState()); + assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_OFF); + assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_OFF, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_DOZE_SUSPEND); + assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_DOZE_SUSPEND, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + // STATE_VR note should map to STATE_ON. + bi.noteScreenStateLocked(0, Display.STATE_VR); + assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_ON, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + // STATE_ON_SUSPEND note should map to STATE_ON. + bi.noteScreenStateLocked(0, Display.STATE_ON_SUSPEND); + assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_ON, bi.getScreenState()); + // Transition from ON to ON state should not cause an External Sync + assertEquals(0, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(1, Display.STATE_DOZE); + assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning()); + // Should remain STATE_ON since display0 is still on. + assertEquals(Display.STATE_ON, bi.getScreenState()); + // Overall screen state did not change, so no need to sync CPU stats. + assertEquals(UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_DOZE); + assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_DOZE, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_ON); + assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_ON, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_OFF); + assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_DOZE, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_DOZE_SUSPEND); + assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_DOZE, bi.getScreenState()); + // Overall screen state did not change, so no need to sync CPU stats. + assertEquals(UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_VR); + assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_ON, bi.getScreenState()); + assertEquals(UPDATE_CPU | UPDATE_DISPLAY, bi.getAndClearExternalStatsSyncFlags()); + + bi.noteScreenStateLocked(0, Display.STATE_ON_SUSPEND); + assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning()); + assertEquals(Display.STATE_ON, bi.getScreenState()); + assertEquals(0, bi.getAndClearExternalStatsSyncFlags()); } /* @@ -352,32 +470,317 @@ public class BatteryStatsNoteTest extends TestCase { bi.updateTimeBasesLocked(true, Display.STATE_UNKNOWN, 100_000, 100_000); // Turn on display at 200us clocks.realtime = clocks.uptime = 200; - bi.noteScreenStateLocked(Display.STATE_ON); + bi.noteScreenStateLocked(0, Display.STATE_ON); assertEquals(150_000, bi.computeBatteryRealtime(250_000, STATS_SINCE_CHARGED)); assertEquals(100_000, bi.computeBatteryScreenOffRealtime(250_000, STATS_SINCE_CHARGED)); assertEquals(50_000, bi.getScreenOnTime(250_000, STATS_SINCE_CHARGED)); assertEquals(0, bi.getScreenDozeTime(250_000, STATS_SINCE_CHARGED)); + assertEquals(50_000, bi.getDisplayScreenOnTime(0, 250_000)); + assertEquals(0, bi.getDisplayScreenDozeTime(0, 250_000)); clocks.realtime = clocks.uptime = 310; - bi.noteScreenStateLocked(Display.STATE_OFF); + bi.noteScreenStateLocked(0, Display.STATE_OFF); assertEquals(250_000, bi.computeBatteryRealtime(350_000, STATS_SINCE_CHARGED)); assertEquals(140_000, bi.computeBatteryScreenOffRealtime(350_000, STATS_SINCE_CHARGED)); assertEquals(110_000, bi.getScreenOnTime(350_000, STATS_SINCE_CHARGED)); assertEquals(0, bi.getScreenDozeTime(350_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getDisplayScreenOnTime(0, 350_000)); + assertEquals(0, bi.getDisplayScreenDozeTime(0, 350_000)); clocks.realtime = clocks.uptime = 400; - bi.noteScreenStateLocked(Display.STATE_DOZE); + bi.noteScreenStateLocked(0, Display.STATE_DOZE); assertEquals(400_000, bi.computeBatteryRealtime(500_000, STATS_SINCE_CHARGED)); assertEquals(290_000, bi.computeBatteryScreenOffRealtime(500_000, STATS_SINCE_CHARGED)); assertEquals(110_000, bi.getScreenOnTime(500_000, STATS_SINCE_CHARGED)); assertEquals(100_000, bi.getScreenDozeTime(500_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getDisplayScreenOnTime(0, 500_000)); + assertEquals(100_000, bi.getDisplayScreenDozeTime(0, 500_000)); clocks.realtime = clocks.uptime = 1000; - bi.noteScreenStateLocked(Display.STATE_OFF); + bi.noteScreenStateLocked(0, Display.STATE_OFF); assertEquals(1400_000, bi.computeBatteryRealtime(1500_000, STATS_SINCE_CHARGED)); assertEquals(1290_000, bi.computeBatteryScreenOffRealtime(1500_000, STATS_SINCE_CHARGED)); assertEquals(110_000, bi.getScreenOnTime(1500_000, STATS_SINCE_CHARGED)); assertEquals(600_000, bi.getScreenDozeTime(1500_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getDisplayScreenOnTime(0, 1500_000)); + assertEquals(600_000, bi.getDisplayScreenDozeTime(0, 1500_000)); + } + + /* + * Test BatteryStatsImpl.noteScreenStateLocked updates timers correctly for multi display + * devices. + */ + @SmallTest + public void testNoteScreenStateTimersLocked_multiDisplay() throws Exception { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.setDisplayCountLocked(2); + + clocks.realtime = clocks.uptime = 100; + // Device startup, setOnBatteryLocked calls updateTimebases + bi.updateTimeBasesLocked(true, Display.STATE_UNKNOWN, 100_000, 100_000); + // Turn on display at 200us + clocks.realtime = clocks.uptime = 200; + bi.noteScreenStateLocked(0, Display.STATE_ON); + bi.noteScreenStateLocked(1, Display.STATE_OFF); + assertEquals(150_000, bi.computeBatteryRealtime(250_000, STATS_SINCE_CHARGED)); + assertEquals(100_000, bi.computeBatteryScreenOffRealtime(250_000, STATS_SINCE_CHARGED)); + assertEquals(50_000, bi.getScreenOnTime(250_000, STATS_SINCE_CHARGED)); + assertEquals(0, bi.getScreenDozeTime(250_000, STATS_SINCE_CHARGED)); + assertEquals(50_000, bi.getDisplayScreenOnTime(0, 250_000)); + assertEquals(0, bi.getDisplayScreenDozeTime(0, 250_000)); + assertEquals(0, bi.getDisplayScreenOnTime(1, 250_000)); + assertEquals(0, bi.getDisplayScreenDozeTime(1, 250_000)); + + clocks.realtime = clocks.uptime = 310; + bi.noteScreenStateLocked(0, Display.STATE_OFF); + assertEquals(250_000, bi.computeBatteryRealtime(350_000, STATS_SINCE_CHARGED)); + assertEquals(140_000, bi.computeBatteryScreenOffRealtime(350_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getScreenOnTime(350_000, STATS_SINCE_CHARGED)); + assertEquals(0, bi.getScreenDozeTime(350_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getDisplayScreenOnTime(0, 350_000)); + assertEquals(0, bi.getDisplayScreenDozeTime(0, 350_000)); + assertEquals(0, bi.getDisplayScreenOnTime(1, 350_000)); + assertEquals(0, bi.getDisplayScreenDozeTime(1, 350_000)); + + clocks.realtime = clocks.uptime = 400; + bi.noteScreenStateLocked(0, Display.STATE_DOZE); + assertEquals(400_000, bi.computeBatteryRealtime(500_000, STATS_SINCE_CHARGED)); + assertEquals(290_000, bi.computeBatteryScreenOffRealtime(500_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getScreenOnTime(500_000, STATS_SINCE_CHARGED)); + assertEquals(100_000, bi.getScreenDozeTime(500_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getDisplayScreenOnTime(0, 500_000)); + assertEquals(100_000, bi.getDisplayScreenDozeTime(0, 500_000)); + assertEquals(0, bi.getDisplayScreenOnTime(1, 500_000)); + assertEquals(0, bi.getDisplayScreenDozeTime(1, 500_000)); + + clocks.realtime = clocks.uptime = 1000; + bi.noteScreenStateLocked(0, Display.STATE_OFF); + assertEquals(1000_000, bi.computeBatteryRealtime(1100_000, STATS_SINCE_CHARGED)); + assertEquals(890_000, bi.computeBatteryScreenOffRealtime(1100_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getScreenOnTime(1100_000, STATS_SINCE_CHARGED)); + assertEquals(600_000, bi.getScreenDozeTime(1100_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getDisplayScreenOnTime(0, 1100_000)); + assertEquals(600_000, bi.getDisplayScreenDozeTime(0, 1100_000)); + assertEquals(0, bi.getDisplayScreenOnTime(1, 1100_000)); + assertEquals(0, bi.getDisplayScreenDozeTime(1, 1100_000)); + + clocks.realtime = clocks.uptime = 1200; + // Change state of second display to doze + bi.noteScreenStateLocked(1, Display.STATE_DOZE); + assertEquals(1150_000, bi.computeBatteryRealtime(1250_000, STATS_SINCE_CHARGED)); + assertEquals(1040_000, bi.computeBatteryScreenOffRealtime(1250_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getScreenOnTime(1250_000, STATS_SINCE_CHARGED)); + assertEquals(650_000, bi.getScreenDozeTime(1250_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getDisplayScreenOnTime(0, 1250_000)); + assertEquals(600_000, bi.getDisplayScreenDozeTime(0, 1250_000)); + assertEquals(0, bi.getDisplayScreenOnTime(1, 1250_000)); + assertEquals(50_000, bi.getDisplayScreenDozeTime(1, 1250_000)); + + clocks.realtime = clocks.uptime = 1310; + bi.noteScreenStateLocked(0, Display.STATE_ON); + assertEquals(1250_000, bi.computeBatteryRealtime(1350_000, STATS_SINCE_CHARGED)); + assertEquals(1100_000, bi.computeBatteryScreenOffRealtime(1350_000, STATS_SINCE_CHARGED)); + assertEquals(150_000, bi.getScreenOnTime(1350_000, STATS_SINCE_CHARGED)); + assertEquals(710_000, bi.getScreenDozeTime(1350_000, STATS_SINCE_CHARGED)); + assertEquals(150_000, bi.getDisplayScreenOnTime(0, 1350_000)); + assertEquals(600_000, bi.getDisplayScreenDozeTime(0, 1350_000)); + assertEquals(0, bi.getDisplayScreenOnTime(1, 1350_000)); + assertEquals(150_000, bi.getDisplayScreenDozeTime(1, 1350_000)); + + clocks.realtime = clocks.uptime = 1400; + bi.noteScreenStateLocked(0, Display.STATE_DOZE); + assertEquals(1400_000, bi.computeBatteryRealtime(1500_000, STATS_SINCE_CHARGED)); + assertEquals(1200_000, bi.computeBatteryScreenOffRealtime(1500_000, STATS_SINCE_CHARGED)); + assertEquals(200_000, bi.getScreenOnTime(1500_000, STATS_SINCE_CHARGED)); + assertEquals(810_000, bi.getScreenDozeTime(1500_000, STATS_SINCE_CHARGED)); + assertEquals(200_000, bi.getDisplayScreenOnTime(0, 1500_000)); + assertEquals(700_000, bi.getDisplayScreenDozeTime(0, 1500_000)); + assertEquals(0, bi.getDisplayScreenOnTime(1, 1500_000)); + assertEquals(300_000, bi.getDisplayScreenDozeTime(1, 1500_000)); + + clocks.realtime = clocks.uptime = 2000; + bi.noteScreenStateLocked(0, Display.STATE_OFF); + assertEquals(2000_000, bi.computeBatteryRealtime(2100_000, STATS_SINCE_CHARGED)); + assertEquals(1800_000, bi.computeBatteryScreenOffRealtime(2100_000, STATS_SINCE_CHARGED)); + assertEquals(200_000, bi.getScreenOnTime(2100_000, STATS_SINCE_CHARGED)); + assertEquals(1410_000, bi.getScreenDozeTime(2100_000, STATS_SINCE_CHARGED)); + assertEquals(200_000, bi.getDisplayScreenOnTime(0, 2100_000)); + assertEquals(1200_000, bi.getDisplayScreenDozeTime(0, 2100_000)); + assertEquals(0, bi.getDisplayScreenOnTime(1, 2100_000)); + assertEquals(900_000, bi.getDisplayScreenDozeTime(1, 2100_000)); + + + clocks.realtime = clocks.uptime = 2200; + // Change state of second display to on + bi.noteScreenStateLocked(1, Display.STATE_ON); + assertEquals(2150_000, bi.computeBatteryRealtime(2250_000, STATS_SINCE_CHARGED)); + assertEquals(1900_000, bi.computeBatteryScreenOffRealtime(2250_000, STATS_SINCE_CHARGED)); + assertEquals(250_000, bi.getScreenOnTime(2250_000, STATS_SINCE_CHARGED)); + assertEquals(1510_000, bi.getScreenDozeTime(2250_000, STATS_SINCE_CHARGED)); + assertEquals(200_000, bi.getDisplayScreenOnTime(0, 2250_000)); + assertEquals(1200_000, bi.getDisplayScreenDozeTime(0, 2250_000)); + assertEquals(50_000, bi.getDisplayScreenOnTime(1, 2250_000)); + assertEquals(1000_000, bi.getDisplayScreenDozeTime(1, 2250_000)); + + clocks.realtime = clocks.uptime = 2310; + bi.noteScreenStateLocked(0, Display.STATE_ON); + assertEquals(2250_000, bi.computeBatteryRealtime(2350_000, STATS_SINCE_CHARGED)); + assertEquals(1900_000, bi.computeBatteryScreenOffRealtime(2350_000, STATS_SINCE_CHARGED)); + assertEquals(350_000, bi.getScreenOnTime(2350_000, STATS_SINCE_CHARGED)); + assertEquals(1510_000, bi.getScreenDozeTime(2350_000, STATS_SINCE_CHARGED)); + assertEquals(240_000, bi.getDisplayScreenOnTime(0, 2350_000)); + assertEquals(1200_000, bi.getDisplayScreenDozeTime(0, 2350_000)); + assertEquals(150_000, bi.getDisplayScreenOnTime(1, 2350_000)); + assertEquals(1000_000, bi.getDisplayScreenDozeTime(1, 2350_000)); + + clocks.realtime = clocks.uptime = 2400; + bi.noteScreenStateLocked(0, Display.STATE_DOZE); + assertEquals(2400_000, bi.computeBatteryRealtime(2500_000, STATS_SINCE_CHARGED)); + assertEquals(1900_000, bi.computeBatteryScreenOffRealtime(2500_000, STATS_SINCE_CHARGED)); + assertEquals(500_000, bi.getScreenOnTime(2500_000, STATS_SINCE_CHARGED)); + assertEquals(1510_000, bi.getScreenDozeTime(2500_000, STATS_SINCE_CHARGED)); + assertEquals(290_000, bi.getDisplayScreenOnTime(0, 2500_000)); + assertEquals(1300_000, bi.getDisplayScreenDozeTime(0, 2500_000)); + assertEquals(300_000, bi.getDisplayScreenOnTime(1, 2500_000)); + assertEquals(1000_000, bi.getDisplayScreenDozeTime(1, 2500_000)); + + clocks.realtime = clocks.uptime = 3000; + bi.noteScreenStateLocked(0, Display.STATE_OFF); + assertEquals(3000_000, bi.computeBatteryRealtime(3100_000, STATS_SINCE_CHARGED)); + assertEquals(1900_000, bi.computeBatteryScreenOffRealtime(3100_000, STATS_SINCE_CHARGED)); + assertEquals(1100_000, bi.getScreenOnTime(3100_000, STATS_SINCE_CHARGED)); + assertEquals(1510_000, bi.getScreenDozeTime(3100_000, STATS_SINCE_CHARGED)); + assertEquals(290_000, bi.getDisplayScreenOnTime(0, 3100_000)); + assertEquals(1800_000, bi.getDisplayScreenDozeTime(0, 3100_000)); + assertEquals(900_000, bi.getDisplayScreenOnTime(1, 3100_000)); + assertEquals(1000_000, bi.getDisplayScreenDozeTime(1, 3100_000)); + } + + + /** + * Test BatteryStatsImpl.noteScreenBrightnessLocked updates timers correctly. + */ + @SmallTest + public void testScreenBrightnessLocked_multiDisplay() throws Exception { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + + final int numDisplay = 2; + bi.setDisplayCountLocked(numDisplay); + + + final long[] overallExpected = new long[NUM_SCREEN_BRIGHTNESS_BINS]; + final long[][] perDisplayExpected = new long[numDisplay][NUM_SCREEN_BRIGHTNESS_BINS]; + class Bookkeeper { + public long currentTimeMs = 100; + public int overallActiveBin = -1; + public int[] perDisplayActiveBin = new int[numDisplay]; + } + final Bookkeeper bk = new Bookkeeper(); + Arrays.fill(bk.perDisplayActiveBin, -1); + + IntConsumer incrementTime = inc -> { + bk.currentTimeMs += inc; + if (bk.overallActiveBin >= 0) { + overallExpected[bk.overallActiveBin] += inc; + } + for (int i = 0; i < numDisplay; i++) { + final int bin = bk.perDisplayActiveBin[i]; + if (bin >= 0) { + perDisplayExpected[i][bin] += inc; + } + } + clocks.realtime = clocks.uptime = bk.currentTimeMs; + }; + + bi.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); + bi.noteScreenStateLocked(0, Display.STATE_ON); + bi.noteScreenStateLocked(1, Display.STATE_ON); + + incrementTime.accept(100); + bi.noteScreenBrightnessLocked(0, 25); + bi.noteScreenBrightnessLocked(1, 25); + // floor(25/256*5) = bin 0 + bk.overallActiveBin = 0; + bk.perDisplayActiveBin[0] = 0; + bk.perDisplayActiveBin[1] = 0; + + incrementTime.accept(50); + checkScreenBrightnesses(overallExpected, perDisplayExpected, bi, bk.currentTimeMs); + + incrementTime.accept(13); + bi.noteScreenBrightnessLocked(0, 100); + // floor(25/256*5) = bin 1 + bk.overallActiveBin = 1; + bk.perDisplayActiveBin[0] = 1; + + incrementTime.accept(44); + checkScreenBrightnesses(overallExpected, perDisplayExpected, bi, bk.currentTimeMs); + + incrementTime.accept(22); + bi.noteScreenBrightnessLocked(1, 200); + // floor(200/256*5) = bin 3 + bk.overallActiveBin = 3; + bk.perDisplayActiveBin[1] = 3; + + incrementTime.accept(33); + checkScreenBrightnesses(overallExpected, perDisplayExpected, bi, bk.currentTimeMs); + + incrementTime.accept(77); + bi.noteScreenBrightnessLocked(0, 150); + // floor(150/256*5) = bin 2 + // Overall active bin should not change + bk.perDisplayActiveBin[0] = 2; + + incrementTime.accept(88); + checkScreenBrightnesses(overallExpected, perDisplayExpected, bi, bk.currentTimeMs); + + incrementTime.accept(11); + bi.noteScreenStateLocked(1, Display.STATE_OFF); + // Display 1 should timers should stop incrementing + // Overall active bin should fallback to display 0's bin + bk.overallActiveBin = 2; + bk.perDisplayActiveBin[1] = -1; + + incrementTime.accept(99); + checkScreenBrightnesses(overallExpected, perDisplayExpected, bi, bk.currentTimeMs); + + incrementTime.accept(200); + bi.noteScreenBrightnessLocked(0, 255); + // floor(150/256*5) = bin 4 + bk.overallActiveBin = 4; + bk.perDisplayActiveBin[0] = 4; + + incrementTime.accept(300); + checkScreenBrightnesses(overallExpected, perDisplayExpected, bi, bk.currentTimeMs); + + incrementTime.accept(200); + bi.noteScreenStateLocked(0, Display.STATE_DOZE); + // No displays are on. No brightness timers should be active. + bk.overallActiveBin = -1; + bk.perDisplayActiveBin[0] = -1; + + incrementTime.accept(300); + checkScreenBrightnesses(overallExpected, perDisplayExpected, bi, bk.currentTimeMs); + + incrementTime.accept(400); + bi.noteScreenStateLocked(1, Display.STATE_ON); + // Display 1 turned back on. + bk.overallActiveBin = 3; + bk.perDisplayActiveBin[1] = 3; + + incrementTime.accept(500); + checkScreenBrightnesses(overallExpected, perDisplayExpected, bi, bk.currentTimeMs); + + incrementTime.accept(600); + bi.noteScreenStateLocked(0, Display.STATE_ON); + // Display 0 turned back on. + bk.overallActiveBin = 4; + bk.perDisplayActiveBin[0] = 4; + + incrementTime.accept(700); + checkScreenBrightnesses(overallExpected, perDisplayExpected, bi, bk.currentTimeMs); } @SmallTest @@ -822,4 +1225,19 @@ public class BatteryStatsNoteTest extends TestCase { assertEquals("Wrong uid2 blame in bucket 1 for Case " + caseName, blame2B, actualUid2[1]); } + + private void checkScreenBrightnesses(long[] overallExpected, long[][] perDisplayExpected, + BatteryStatsImpl bi, long currentTimeMs) { + final int numDisplay = bi.getDisplayCount(); + for (int bin = 0; bin < NUM_SCREEN_BRIGHTNESS_BINS; bin++) { + for (int display = 0; display < numDisplay; display++) { + assertEquals("Failure for display " + display + " screen brightness bin " + bin, + perDisplayExpected[display][bin] * 1000, + bi.getDisplayScreenBrightnessTime(display, bin, currentTimeMs * 1000)); + } + assertEquals("Failure for overall screen brightness bin " + bin, + overallExpected[bin] * 1000, + bi.getScreenBrightnessTime(bin, currentTimeMs * 1000, STATS_SINCE_CHARGED)); + } + } } diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index cee1a0352a7ef..cfecf15b55eff 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -39,13 +39,14 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { public BatteryStatsImpl.Clocks clocks; public boolean mForceOnBattery; private NetworkStats mNetworkStats; + private DummyExternalStatsSync mExternalStatsSync = new DummyExternalStatsSync(); MockBatteryStatsImpl(Clocks clocks) { super(clocks); this.clocks = mClocks; initTimersAndCounters(); - setExternalStatsSyncLocked(new DummyExternalStatsSync()); + setExternalStatsSyncLocked(mExternalStatsSync); informThatAllExternalStatsAreFlushed(); // A no-op handler. @@ -182,7 +183,15 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { return mPendingUids; } + public int getAndClearExternalStatsSyncFlags() { + final int flags = mExternalStatsSync.flags; + mExternalStatsSync.flags = 0; + return flags; + } + private class DummyExternalStatsSync implements ExternalStatsSync { + public int flags = 0; + @Override public Future scheduleSync(String reason, int flags) { return null; @@ -211,8 +220,9 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { } @Override - public Future scheduleSyncDueToScreenStateChange( - int flag, boolean onBattery, boolean onBatteryScreenOff, int screenState) { + public Future scheduleSyncDueToScreenStateChange(int flag, boolean onBattery, + boolean onBatteryScreenOff, int screenState, int[] perDisplayScreenStates) { + flags |= flag; return null; } 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 50e0a15128191..73f4eb2d2be86 100644 --- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java @@ -53,7 +53,7 @@ public class ScreenPowerCalculatorTest { mStatsRule.initMeasuredEnergyStatsLocked(); BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); - batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0); + batteryStats.noteScreenStateLocked(0, Display.STATE_ON, 0, 0, 0); batteryStats.updateDisplayMeasuredEnergyStatsLocked(0, Display.STATE_ON, 0); setProcState(APP_UID1, ActivityManager.PROCESS_STATE_TOP, true, 0, 0); @@ -70,7 +70,7 @@ public class ScreenPowerCalculatorTest { batteryStats.updateDisplayMeasuredEnergyStatsLocked(300_000_000, Display.STATE_ON, 60 * MINUTE_IN_MS); - batteryStats.noteScreenStateLocked(Display.STATE_OFF, + 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); @@ -133,20 +133,20 @@ public class ScreenPowerCalculatorTest { public void testPowerProfileBasedModel() { BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); - batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0); - batteryStats.noteScreenBrightnessLocked(255, 0, 0); + batteryStats.noteScreenStateLocked(0, Display.STATE_ON, 0, 0, 0); + batteryStats.noteScreenBrightnessLocked(0, 255, 0, 0); setProcState(APP_UID1, ActivityManager.PROCESS_STATE_TOP, true, 0, 0); - batteryStats.noteScreenBrightnessLocked(100, 5 * MINUTE_IN_MS, 5 * MINUTE_IN_MS); - batteryStats.noteScreenBrightnessLocked(200, 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + 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); - batteryStats.noteScreenStateLocked(Display.STATE_OFF, + 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); diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index e9aa3be8b403d..6374d34294d5b 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -113,6 +113,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { @GuardedBy("this") private int mScreenState; + @GuardedBy("this") + private int[] mPerDisplayScreenStates = null; + @GuardedBy("this") private boolean mUseLatestStates = true; @@ -291,8 +294,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } @Override - public Future scheduleSyncDueToScreenStateChange( - int flags, boolean onBattery, boolean onBatteryScreenOff, int screenState) { + public Future scheduleSyncDueToScreenStateChange(int flags, boolean onBattery, + boolean onBatteryScreenOff, int screenState, int[] perDisplayScreenStates) { synchronized (BatteryExternalStatsWorker.this) { if (mCurrentFuture == null || (mUpdateFlags & UPDATE_CPU) == 0) { mOnBattery = onBattery; @@ -301,6 +304,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } // always update screen state mScreenState = screenState; + mPerDisplayScreenStates = perDisplayScreenStates; return scheduleSyncLocked("screen-state", flags); } } @@ -432,6 +436,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { final boolean onBattery; final boolean onBatteryScreenOff; final int screenState; + final int[] displayScreenStates; final boolean useLatestStates; synchronized (BatteryExternalStatsWorker.this) { updateFlags = mUpdateFlags; @@ -440,6 +445,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { onBattery = mOnBattery; onBatteryScreenOff = mOnBatteryScreenOff; screenState = mScreenState; + displayScreenStates = mPerDisplayScreenStates; useLatestStates = mUseLatestStates; mUpdateFlags = 0; mCurrentReason = null; @@ -461,7 +467,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } try { updateExternalStatsLocked(reason, updateFlags, onBattery, - onBatteryScreenOff, screenState, useLatestStates); + onBatteryScreenOff, screenState, displayScreenStates, + useLatestStates); } finally { if (DEBUG) { Slog.d(TAG, "end updateExternalStatsSync"); @@ -506,7 +513,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { @GuardedBy("mWorkerLock") private void updateExternalStatsLocked(final String reason, int updateFlags, boolean onBattery, - boolean onBatteryScreenOff, int screenState, boolean useLatestStates) { + boolean onBatteryScreenOff, int screenState, int[] displayScreenStates, + boolean useLatestStates) { // We will request data from external processes asynchronously, and wait on a timeout. SynchronousResultReceiver wifiReceiver = null; SynchronousResultReceiver bluetoothReceiver = null; @@ -661,7 +669,8 @@ 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. + // 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, diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 9a349ef499fcd..cd9eb9037c7fe 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -1215,7 +1215,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub mHandler.post(() -> { if (DBG) Slog.d(TAG, "begin noteScreenState"); synchronized (mStats) { - mStats.noteScreenStateLocked(state, elapsedRealtime, uptime, currentTime); + mStats.noteScreenStateLocked(0, state, elapsedRealtime, uptime, currentTime); } if (DBG) Slog.d(TAG, "end noteScreenState"); }); @@ -1230,7 +1230,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteScreenBrightnessLocked(brightness, elapsedRealtime, uptime); + mStats.noteScreenBrightnessLocked(0, brightness, elapsedRealtime, uptime); } }); } diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java index 8a8a6246b73b1..6faa7e7c89e75 100644 --- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java @@ -202,6 +202,7 @@ public class BatteryExternalStatsWorkerTest { public class TestBatteryStatsImpl extends BatteryStatsImpl { public TestBatteryStatsImpl(Context context) { mPowerProfile = new PowerProfile(context, true /* forTest */); + initTimersAndCounters(); } }