Update BatteryStatsImpl to handle multidisplay

Add screen on and doze timers for multiple displays.

For Battery history, charge/discharge logs and UnsupportedAppUsage, the
legacy screen state timers remain, as well. The legacy timers and
screen state tracking will now track overall screen state of the device.
The current overall screen state is the most "important" state currently
active.
Order of screen state importance is: on, doze, doze suspend, off.

Note: Utilizing per display measured energy will be handled in a follow
up CL.

Bug: 194107383
Test: atest FrameworksCoreTests:BatteryStatsTests
Change-Id: I4ad4f54b6897f406e4ec0f59834dd4762083e0d8
This commit is contained in:
Michael Wachenschwanz
2021-09-24 00:26:37 -07:00
parent c1e6b2dd8c
commit 5f05b4ffcf
8 changed files with 923 additions and 62 deletions

View File

@@ -2308,6 +2308,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.
@@ -5044,6 +5076,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);

View File

@@ -678,7 +678,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);
@@ -844,17 +844,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(Clock clock, TimeBase timeBase) {
screenOnTimer = new StopwatchTimer(clock, null, -1, null,
timeBase);
screenDozeTimer = new StopwatchTimer(clock, null, -1, null,
timeBase);
for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; i++) {
screenBrightnessTimers[i] = new StopwatchTimer(clock, 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;
@@ -4455,8 +4522,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void setPretendScreenOff(boolean pretendScreenOff) {
if (mPretendScreenOff != pretendScreenOff) {
mPretendScreenOff = pretendScreenOff;
noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON,
mClock.elapsedRealtime(), mClock.uptimeMillis(), mClock.currentTimeMillis());
final int primaryScreenState = mPerDisplayBatteryStats[0].screenState;
noteScreenStateLocked(0, primaryScreenState,
mClock.elapsedRealtime(), mClock.uptimeMillis(),
mClock.currentTimeMillis());
}
}
@@ -5054,29 +5123,158 @@ public class BatteryStatsImpl extends BatteryStats {
}
@GuardedBy("this")
public void noteScreenStateLocked(int state) {
noteScreenStateLocked(state, mClock.elapsedRealtime(), mClock.uptimeMillis(),
public void noteScreenStateLocked(int display, int state) {
noteScreenStateLocked(display, state, mClock.elapsedRealtime(), mClock.uptimeMillis(),
mClock.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;
@@ -5130,11 +5328,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);
@@ -5152,33 +5350,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, mClock.elapsedRealtime(), mClock.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, mClock.elapsedRealtime(),
mClock.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;
}
}
@@ -6842,6 +7123,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);
}
@@ -10875,6 +11181,10 @@ public class BatteryStatsImpl extends BatteryStats {
mScreenBrightnessTimer[i] = new StopwatchTimer(mClock, null, -100 - i, null,
mOnBatteryTimeBase);
}
mPerDisplayBatteryStats = new DisplayBatteryStats[1];
mPerDisplayBatteryStats[0] = new DisplayBatteryStats(mClock, mOnBatteryTimeBase);
mInteractiveTimer = new StopwatchTimer(mClock, null, -10, null, mOnBatteryTimeBase);
mPowerSaveModeEnabledTimer = new StopwatchTimer(mClock, null, -2, null,
mOnBatteryTimeBase);
@@ -10987,6 +11297,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() {
@@ -11019,6 +11331,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(mClock, mOnBatteryTimeBase);
}
}
public void updateDailyDeadlineLocked() {
// Get the current time.
long currentTimeMs = mDailyStartTimeMs = mClock.currentTimeMillis();
@@ -11502,6 +11824,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 {

View File

@@ -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 =

View File

@@ -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 MockClock clocks = new MockClock(); // 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 MockClock clocks = new MockClock(); // 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 MockClock clocks = new MockClock(); // 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 MockClock clocks = new MockClock(); // 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
@@ -820,4 +1223,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));
}
}
}

View File

@@ -39,6 +39,7 @@ import java.util.concurrent.Future;
public class MockBatteryStatsImpl extends BatteryStatsImpl {
public boolean mForceOnBattery;
private NetworkStats mNetworkStats;
private DummyExternalStatsSync mExternalStatsSync = new DummyExternalStatsSync();
MockBatteryStatsImpl() {
this(new MockClock());
@@ -52,7 +53,7 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
super(clock, historyDirectory);
initTimersAndCounters();
setExternalStatsSyncLocked(new DummyExternalStatsSync());
setExternalStatsSyncLocked(mExternalStatsSync);
informThatAllExternalStatsAreFlushed();
// A no-op handler.
@@ -185,7 +186,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;
@@ -219,8 +228,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;
}

View File

@@ -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);

View File

@@ -116,6 +116,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
@GuardedBy("this")
private int mScreenState;
@GuardedBy("this")
private int[] mPerDisplayScreenStates = null;
@GuardedBy("this")
private boolean mUseLatestStates = true;
@@ -294,8 +297,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;
@@ -304,6 +307,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
// always update screen state
mScreenState = screenState;
mPerDisplayScreenStates = perDisplayScreenStates;
return scheduleSyncLocked("screen-state", flags);
}
}
@@ -446,6 +450,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;
@@ -454,6 +459,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
onBattery = mOnBattery;
onBatteryScreenOff = mOnBatteryScreenOff;
screenState = mScreenState;
displayScreenStates = mPerDisplayScreenStates;
useLatestStates = mUseLatestStates;
mUpdateFlags = 0;
mCurrentReason = null;
@@ -475,7 +481,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");
@@ -520,7 +527,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;
@@ -675,7 +683,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,

View File

@@ -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);
}
});
}