Separate refresh rate restrictions for different High Brightness Modes

Allow setting a different refresh rate for High Brightness Mode for HDR
and for Sunlight.

Bug: 196691596
Test: atest DisplayModeDirectorTest
Test: Manual:
      1. Ensure HBM for HDR takes presedence over HBM for Sunlight
      2. Ensure app refresh rate votes override bothHBM for Sunlight
Change-Id: I4a29c47f218046d0852178ad5dc7484a86ef8607
Merged-In: I4a29c47f218046d0852178ad5dc7484a86ef8607
This commit is contained in:
Daniel Solomon
2021-09-26 20:57:13 -07:00
committed by Yichi Chen
parent 7e783bb071
commit 46fff422b4
5 changed files with 326 additions and 31 deletions

View File

@@ -1257,6 +1257,23 @@ public final class DisplayManager {
*/
String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS =
"fixed_refresh_rate_high_ambient_brightness_thresholds";
/**
* Key for refresh rate when the device is in high brightness mode for sunlight visility.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.integer#config_defaultRefreshRateInHbmSunlight
*/
String KEY_REFRESH_RATE_IN_HBM_SUNLIGHT = "refresh_rate_in_hbm_sunlight";
/**
* Key for refresh rate when the device is in high brightness mode for HDR.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.integer#config_defaultRefreshRateInHbmHdr
*/
String KEY_REFRESH_RATE_IN_HBM_HDR = "refresh_rate_in_hbm_hdr";
/**
* Key for default peak refresh rate
*

View File

@@ -4492,6 +4492,13 @@
If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
<integer name="config_fixedRefreshRateInHighZone">0</integer>
<!-- Default refresh rate while the device has high brightness mode enabled for Sunlight.
This value overrides values from DisplayDeviceConfig -->
<integer name="config_defaultRefreshRateInHbmSunlight">0</integer>
<!-- Default refresh rate while the device has high brightness mode enabled for HDR. -->
<integer name="config_defaultRefreshRateInHbmHdr">0</integer>
<!-- The type of the light sensor to be used by the display framework for things like
auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. -->
<string name="config_displayLightSensorType" translatable="false" />

View File

@@ -3948,6 +3948,8 @@
<java-symbol type="integer" name="config_defaultRefreshRateInZone" />
<java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
<java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
<java-symbol type="integer" name="config_defaultRefreshRateInHbmSunlight" />
<java-symbol type="integer" name="config_defaultRefreshRateInHbmHdr" />
<!-- For fixed refresh rate displays in high brightness-->
<java-symbol type="integer" name="config_fixedRefreshRateInHighZone" />

View File

@@ -53,6 +53,7 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayInfo;
@@ -77,7 +78,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
/**
* The DisplayModeDirector is responsible for determining what modes are allowed to be automatically
* picked by the system based on system-wide and display-specific configuration.
@@ -92,6 +92,8 @@ public class DisplayModeDirector {
private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
private static final int MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED = 7;
private static final int MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED = 8;
// Special ID used to indicate that given vote is to be applied globally, rather than to a
// specific display.
@@ -161,9 +163,10 @@ public class DisplayModeDirector {
}
};
mSensorObserver = new SensorObserver(context, ballotBox, injector);
mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, ballotBox);
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler(),
mDeviceConfigDisplaySettings);
mDeviceConfig = injector.getDeviceConfig();
mAlwaysRespectAppRequest = false;
}
@@ -723,6 +726,11 @@ public class DisplayModeDirector {
return mUdfpsObserver;
}
@VisibleForTesting
HbmObserver getHbmObserver() {
return mHbmObserver;
}
@VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
@@ -792,6 +800,19 @@ public class DisplayModeDirector {
(DesiredDisplayModeSpecsListener) msg.obj;
desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged();
break;
case MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED: {
int refreshRateInHbmSunlight = msg.arg1;
mHbmObserver.onDeviceConfigRefreshRateInHbmSunlightChanged(
refreshRateInHbmSunlight);
break;
}
case MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED: {
int refreshRateInHbmHdr = msg.arg1;
mHbmObserver.onDeviceConfigRefreshRateInHbmHdrChanged(refreshRateInHbmHdr);
break;
}
}
}
}
@@ -918,16 +939,19 @@ public class DisplayModeDirector {
// result is a range.
public static final int PRIORITY_FLICKER_REFRESH_RATE = 1;
// High-brightness-mode may need a specific range of refresh-rates to function properly.
public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
// SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
// It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2;
public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 3;
// APP_REQUEST_REFRESH_RATE_RANGE is used to for internal apps to limit the refresh
// rate in certain cases, mostly to preserve power.
// @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
// @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
// It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
public static final int PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE = 3;
public static final int PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE = 4;
// We split the app request into different priorities in case we can satisfy one desire
// without the other.
@@ -942,27 +966,24 @@ public class DisplayModeDirector {
// The preferred refresh rate is set on the main surface of the app outside of
// DisplayModeDirector.
// @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 4;
public static final int PRIORITY_APP_REQUEST_SIZE = 5;
public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5;
public static final int PRIORITY_APP_REQUEST_SIZE = 6;
// SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
// of low priority voters. It votes [0, max(PEAK, MIN)]
public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 6;
public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 7;
// LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
public static final int PRIORITY_LOW_POWER_MODE = 7;
public static final int PRIORITY_LOW_POWER_MODE = 8;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
// It's used to avoid refresh rate switches in certain conditions which may result in the
// user seeing the display flickering when the switches occur.
public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8;
public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 9;
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
public static final int PRIORITY_SKIN_TEMPERATURE = 9;
// High-brightness-mode may need a specific range of refresh-rates to function properly.
public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 10;
public static final int PRIORITY_SKIN_TEMPERATURE = 10;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
@@ -2258,33 +2279,78 @@ public class DisplayModeDirector {
* HBM that are associated with that display. Restrictions are retrieved from
* DisplayManagerInternal but originate in the display-device-config file.
*/
private static class HbmObserver implements DisplayManager.DisplayListener {
public static class HbmObserver implements DisplayManager.DisplayListener {
private final BallotBox mBallotBox;
private final Handler mHandler;
private final SparseBooleanArray mHbmEnabled = new SparseBooleanArray();
private final SparseIntArray mHbmMode = new SparseIntArray();
private final Injector mInjector;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
private int mRefreshRateInHbmSunlight;
private int mRefreshRateInHbmHdr;
private DisplayManagerInternal mDisplayManagerInternal;
HbmObserver(Injector injector, BallotBox ballotBox, Handler handler) {
HbmObserver(Injector injector, BallotBox ballotBox, Handler handler,
DeviceConfigDisplaySettings displaySettings) {
mInjector = injector;
mBallotBox = ballotBox;
mHandler = handler;
mDeviceConfigDisplaySettings = displaySettings;
}
public void observe() {
mRefreshRateInHbmSunlight = mDeviceConfigDisplaySettings.getRefreshRateInHbmSunlight();
mRefreshRateInHbmHdr = mDeviceConfigDisplaySettings.getRefreshRateInHbmHdr();
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mInjector.registerDisplayListener(this, mHandler,
DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
}
/**
* @return the refresh to lock to when the device is in high brightness mode for Sunlight.
*/
@VisibleForTesting
int getRefreshRateInHbmSunlight() {
return mRefreshRateInHbmSunlight;
}
/**
* @return the refresh to lock to when the device is in high brightness mode for HDR.
*/
@VisibleForTesting
int getRefreshRateInHbmHdr() {
return mRefreshRateInHbmHdr;
}
/**
* Recalculates the HBM vote when the device config has been changed.
*/
public void onDeviceConfigRefreshRateInHbmSunlightChanged(int refreshRate) {
if (refreshRate != mRefreshRateInHbmSunlight) {
mRefreshRateInHbmSunlight = refreshRate;
onDeviceConfigRefreshRateInHbmChanged();
}
}
/**
* Recalculates the HBM vote when the device config has been changed.
*/
public void onDeviceConfigRefreshRateInHbmHdrChanged(int refreshRate) {
if (refreshRate != mRefreshRateInHbmHdr) {
mRefreshRateInHbmHdr = refreshRate;
onDeviceConfigRefreshRateInHbmChanged();
}
}
@Override
public void onDisplayAdded(int displayId) {}
@Override
public void onDisplayRemoved(int displayId) {
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
mHbmMode.delete(displayId);
}
@Override
@@ -2294,31 +2360,56 @@ public class DisplayModeDirector {
// Display no longer there. Assume we'll get an onDisplayRemoved very soon.
return;
}
final boolean isHbmEnabled =
info.highBrightnessMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
if (isHbmEnabled == mHbmEnabled.get(displayId)) {
final int hbmMode = info.highBrightnessMode;
if (hbmMode == mHbmMode.get(displayId)) {
// no change, ignore.
return;
}
mHbmMode.put(displayId, hbmMode);
recalculateVotesForDisplay(displayId);
}
private void onDeviceConfigRefreshRateInHbmChanged() {
final int[] displayIds = mHbmMode.copyKeys();
if (displayIds != null) {
for (int id : displayIds) {
recalculateVotesForDisplay(id);
}
}
}
private void recalculateVotesForDisplay(int displayId) {
final int hbmMode = mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
Vote vote = null;
mHbmEnabled.put(displayId, isHbmEnabled);
if (isHbmEnabled) {
final List<RefreshRateLimitation> limits =
if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
// Device resource properties take priority over DisplayDeviceConfig
if (mRefreshRateInHbmSunlight > 0) {
vote = Vote.forRefreshRates(mRefreshRateInHbmSunlight,
mRefreshRateInHbmSunlight);
} else {
final List<RefreshRateLimitation> limits =
mDisplayManagerInternal.getRefreshRateLimitations(displayId);
for (int i = 0; limits != null && i < limits.size(); i++) {
final RefreshRateLimitation limitation = limits.get(i);
if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
break;
for (int i = 0; limits != null && i < limits.size(); i++) {
final RefreshRateLimitation limitation = limits.get(i);
if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
break;
}
}
}
}
if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
&& mRefreshRateInHbmHdr > 0) {
vote = Vote.forRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr);
}
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
}
void dumpLocked(PrintWriter pw) {
pw.println(" HbmObserver");
pw.println(" mHbmEnabled: " + mHbmEnabled);
pw.println(" mHbmMode: " + mHbmMode);
pw.println(" mRefreshRateInHbmSunlight: " + mRefreshRateInHbmSunlight);
pw.println(" mRefreshRateInHbmHdr: " + mRefreshRateInHbmHdr);
}
}
@@ -2437,6 +2528,29 @@ public class DisplayModeDirector {
return refreshRate;
}
public int getRefreshRateInHbmSunlight() {
final int defaultRefreshRateInHbmSunlight =
mContext.getResources().getInteger(
R.integer.config_defaultRefreshRateInHbmSunlight);
final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT,
defaultRefreshRateInHbmSunlight);
return refreshRate;
}
public int getRefreshRateInHbmHdr() {
final int defaultRefreshRateInHbmHdr =
mContext.getResources().getInteger(R.integer.config_defaultRefreshRateInHbmHdr);
final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR,
defaultRefreshRateInHbmHdr);
return refreshRate;
}
/*
* Return null if no such property
*/
@@ -2476,6 +2590,15 @@ public class DisplayModeDirector {
.sendToTarget();
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
.sendToTarget();
final int refreshRateInHbmSunlight = getRefreshRateInHbmSunlight();
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED,
refreshRateInHbmSunlight, 0)
.sendToTarget();
final int refreshRateInHbmHdr = getRefreshRateInHbmHdr();
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED, refreshRateInHbmHdr, 0)
.sendToTarget();
}
private int[] getIntArrayProperty(String prop) {

View File

@@ -20,6 +20,8 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REF
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
@@ -1408,6 +1410,12 @@ public class DisplayModeDirectorTest {
public void testHbmVoting_forHdr() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
final int hbmRefreshRate = 72;
// Specify limitation before starting DisplayModeDirector to avoid waiting on property
// propagation
mInjector.getDeviceConfig().setRefreshRateInHbmHdr(hbmRefreshRate);
director.start(createMockSensorManager());
ArgumentCaptor<DisplayListener> captor =
@@ -1432,7 +1440,7 @@ public class DisplayModeDirectorTest {
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, 60.f);
assertVoteForRefreshRate(vote, hbmRefreshRate);
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
@@ -1442,6 +1450,44 @@ public class DisplayModeDirectorTest {
assertNull(vote);
}
@Test
public void testHbmObserverGetsUpdatedRefreshRateInHbmSunlight() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
final int initialRefreshRate = 60;
mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(initialRefreshRate);
director.start(createMockSensorManager());
assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
.isEqualTo(initialRefreshRate);
final int updatedRefreshRate = 90;
mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(updatedRefreshRate);
// Need to wait for the property change to propagate to the main thread.
waitForIdleSync();
assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
.isEqualTo(updatedRefreshRate);
}
@Test
public void testHbmObserverGetsUpdatedRefreshRateInHbmHdr() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
final int initialRefreshRate = 60;
mInjector.getDeviceConfig().setRefreshRateInHbmHdr(initialRefreshRate);
director.start(createMockSensorManager());
assertThat(director.getHbmObserver().getRefreshRateInHbmHdr())
.isEqualTo(initialRefreshRate);
final int updatedRefreshRate = 90;
mInjector.getDeviceConfig().setRefreshRateInHbmHdr(updatedRefreshRate);
// Need to wait for the property change to propagate to the main thread.
waitForIdleSync();
assertThat(director.getHbmObserver().getRefreshRateInHbmHdr())
.isEqualTo(updatedRefreshRate);
}
@Test
public void testHbmVoting_forSunlight() {
DisplayModeDirector director =
@@ -1455,11 +1501,12 @@ public class DisplayModeDirectorTest {
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
DisplayListener listener = captor.getValue();
final int initialRefreshRate = 60;
// Specify Limitation
when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
List.of(new RefreshRateLimitation(
DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
60.f, 60.f)));
initialRefreshRate, initialRefreshRate)));
// Verify that there is no HBM vote initially
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
@@ -1470,7 +1517,39 @@ public class DisplayModeDirectorTest {
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, 60.f);
assertVoteForRefreshRate(vote, initialRefreshRate);
// Change refresh rate vote value through DeviceConfig, ensure it takes precedence
final int updatedRefreshRate = 90;
mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(updatedRefreshRate);
// Need to wait for the property change to propagate to the main thread.
waitForIdleSync();
assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
.isEqualTo(updatedRefreshRate);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, updatedRefreshRate);
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
// Turn HBM on again and ensure the updated vote value stuck
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, updatedRefreshRate);
// Reset DeviceConfig refresh rate, ensure vote falls back to the initial value
mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(0);
// Need to wait for the property change to propagate to the main thread.
waitForIdleSync();
assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight()).isEqualTo(0);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, initialRefreshRate);
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
@@ -1518,6 +1597,63 @@ public class DisplayModeDirectorTest {
assertNull(vote);
}
private void setHbmAndAssertRefreshRate(
DisplayModeDirector director, DisplayListener listener, int mode, float rr) {
when(mInjector.getBrightnessInfo(DISPLAY_ID))
.thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode));
listener.onDisplayChanged(DISPLAY_ID);
final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
if (Float.isNaN(rr)) {
assertNull(vote);
} else {
assertVoteForRefreshRate(vote, rr);
}
}
@Test
public void testHbmVoting_forSunlightAndHdr() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
// Specify HDR limitation before starting DisplayModeDirector to avoid waiting on property
// propagation
final int hdrRr = 60;
mInjector.getDeviceConfig().setRefreshRateInHbmHdr(hdrRr);
director.start(createMockSensorManager());
ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
DisplayListener listener = captor.getValue();
// Specify Sunlight limitations
final float sunlightRr = 90.0f;
when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID))
.thenReturn(List.of(new RefreshRateLimitation(
DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, sunlightRr,
sunlightRr)));
// Verify that there is no HBM vote initially
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
// Verify all state transitions
setHbmAndAssertRefreshRate(
director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, sunlightRr);
setHbmAndAssertRefreshRate(
director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, hdrRr);
setHbmAndAssertRefreshRate(
director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, Float.NaN);
setHbmAndAssertRefreshRate(
director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, hdrRr);
setHbmAndAssertRefreshRate(
director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, sunlightRr);
setHbmAndAssertRefreshRate(
director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, Float.NaN);
}
@Test
public void testHbmVoting_RemovedDisplay() {
DisplayModeDirector director =
@@ -1622,6 +1758,16 @@ public class DisplayModeDirectorTest {
String.valueOf(fps));
}
void setRefreshRateInHbmSunlight(int fps) {
putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
KEY_REFRESH_RATE_IN_HBM_SUNLIGHT, String.valueOf(fps));
}
void setRefreshRateInHbmHdr(int fps) {
putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
KEY_REFRESH_RATE_IN_HBM_HDR, String.valueOf(fps));
}
void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) {
String thresholds = toPropertyValue(brightnessThresholds);