diff options
| author | 2021-09-29 13:37:41 +0000 | |
|---|---|---|
| committer | 2021-09-29 13:37:41 +0000 | |
| commit | 51b865a0ec805cadf09a6d1f36560eb8555b4bd1 (patch) | |
| tree | 9d0c1ca5e4f6d3ad882f3e6a4116138e96eba1b7 | |
| parent | c0d03ace54814be9ccb6f55bfc664ea9c65b76fc (diff) | |
| parent | af5c64f76fb18a03a1f426b53dc8600bd8420e01 (diff) | |
Merge "Separate refresh rate restrictions for different High Brightness Modes" into sc-qpr1-dev am: af5c64f76f
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15937366
Change-Id: I9dcfe5d51a3c48a39b3e06ce42d2b88368a08b8d
5 files changed, 326 insertions, 31 deletions
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 73961ff373dd..d8f20391098c 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -1288,6 +1288,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 * diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index cbe0eef6408b..689d75b9cfea 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4533,6 +4533,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" /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index fbc629e2dc81..4e8d915ca295 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3964,6 +3964,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" /> diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 5797b061f2d0..5fc301e60b9d 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -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; } @@ -724,6 +727,11 @@ public class DisplayModeDirector { } @VisibleForTesting + HbmObserver getHbmObserver() { + return mHbmObserver; + } + + @VisibleForTesting DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { synchronized (mLock) { @@ -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) { diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 4564296810ff..0dd5c61121db 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -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( @@ -1443,6 +1451,44 @@ public class DisplayModeDirectorTest { } @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 = createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0); @@ -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); |