diff options
8 files changed, 353 insertions, 19 deletions
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index d647757442e0..6a36fbe06884 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -366,9 +366,10 @@ public class AutomaticBrightnessController { } /** - * @return The current brightness recommendation calculated from the current conditions. - * @param brightnessEvent Event object to populate with details about why the specific - * brightness was chosen. + * @param brightnessEvent Holds details about how the brightness is calculated. + * + * @return The current automatic brightness recommended value. Populates brightnessEvent + * parameters with details about how the brightness was calculated. */ public float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) { if (brightnessEvent != null) { diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 486cd288753e..2464eb0141b8 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -184,6 +184,7 @@ import javax.xml.datatype.DatatypeConfigurationException; * <defaultRefreshRateInHbmSunlight>75</defaultRefreshRateInHbmSunlight> * <lowerBlockingZoneConfigs> * <defaultRefreshRate>75</defaultRefreshRate> + * <refreshRateThermalThrottlingId>id_of_a_throttling_map</refreshRateThermalThrottlingId> * <blockingZoneThreshold> * <displayBrightnessPoint> * <lux>50</lux> @@ -722,6 +723,12 @@ public class DisplayDeviceConfig { private float[] mHighDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS; private float[] mHighAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS; + /** + * Thermal throttling maps for the low and high blocking zones. + */ + private String mLowBlockingZoneThermalMapId = null; + private String mHighBlockingZoneThermalMapId = null; + private final HashMap<String, ThermalBrightnessThrottlingData> mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>(); @@ -1526,6 +1533,13 @@ public class DisplayDeviceConfig { } /** + * @return The refresh rate thermal map for low blocking zone. + */ + public SparseArray<SurfaceControl.RefreshRateRange> getLowBlockingZoneThermalMap() { + return getThermalRefreshRateThrottlingData(mLowBlockingZoneThermalMapId); + } + + /** * @return An array of high display brightness thresholds. This, in combination with high * ambient brightness thresholds help define buckets in which the refresh rate switching is not * allowed. @@ -1548,6 +1562,13 @@ public class DisplayDeviceConfig { } /** + * @return The refresh rate thermal map for high blocking zone. + */ + public SparseArray<SurfaceControl.RefreshRateRange> getHighBlockingZoneThermalMap() { + return getThermalRefreshRateThrottlingData(mHighBlockingZoneThermalMapId); + } + + /** * @return A mapping from screen off brightness sensor readings to lux values. This estimates * the ambient lux when the screen is off to determine the initial brightness */ @@ -1664,6 +1685,8 @@ public class DisplayDeviceConfig { + ", mDefaultRefreshRateInHbmHdr= " + mDefaultRefreshRateInHbmHdr + ", mDefaultRefreshRateInHbmSunlight= " + mDefaultRefreshRateInHbmSunlight + ", mRefreshRateThrottlingMap= " + mRefreshRateThrottlingMap + + ", mLowBlockingZoneThermalMapId= " + mLowBlockingZoneThermalMapId + + ", mHighBlockingZoneThermalMapId= " + mHighBlockingZoneThermalMapId + "\n" + ", mLowDisplayBrightnessThresholds= " + Arrays.toString(mLowDisplayBrightnessThresholds) @@ -2114,9 +2137,13 @@ public class DisplayDeviceConfig { } /** - * Loads the refresh rate configurations pertaining to the upper blocking zones. + * Loads the refresh rate configurations pertaining to the lower blocking zones. */ private void loadLowerRefreshRateBlockingZones(BlockingZoneConfig lowerBlockingZoneConfig) { + if (lowerBlockingZoneConfig != null) { + mLowBlockingZoneThermalMapId = + lowerBlockingZoneConfig.getRefreshRateThermalThrottlingId(); + } loadLowerBlockingZoneDefaultRefreshRate(lowerBlockingZoneConfig); loadLowerBrightnessThresholds(lowerBlockingZoneConfig); } @@ -2125,6 +2152,10 @@ public class DisplayDeviceConfig { * Loads the refresh rate configurations pertaining to the upper blocking zones. */ private void loadHigherRefreshRateBlockingZones(BlockingZoneConfig upperBlockingZoneConfig) { + if (upperBlockingZoneConfig != null) { + mHighBlockingZoneThermalMapId = + upperBlockingZoneConfig.getRefreshRateThermalThrottlingId(); + } loadHigherBlockingZoneDefaultRefreshRate(upperBlockingZoneConfig); loadHigherBrightnessThresholds(upperBlockingZoneConfig); } diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 82755b6a2b9c..6079a3295502 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -1540,6 +1540,21 @@ public class DisplayModeDirector { private final Injector mInjector; private final Handler mHandler; + private final IThermalEventListener.Stub mThermalListener = + new IThermalEventListener.Stub() { + @Override + public void notifyThrottling(Temperature temp) { + @Temperature.ThrottlingStatus int currentStatus = temp.getStatus(); + synchronized (mLock) { + if (mThermalStatus != currentStatus) { + mThermalStatus = currentStatus; + } + onBrightnessChangedLocked(); + } + } + }; + private boolean mThermalRegistered; + // Enable light sensor only when mShouldObserveAmbientLowChange is true or // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate // changeable and low power mode off. After initialization, these states will @@ -1548,9 +1563,17 @@ public class DisplayModeDirector { private boolean mRefreshRateChangeable = false; private boolean mLowPowerModeEnabled = false; + @Nullable + private SparseArray<RefreshRateRange> mLowZoneRefreshRateForThermals; private int mRefreshRateInLowZone; + + @Nullable + private SparseArray<RefreshRateRange> mHighZoneRefreshRateForThermals; private int mRefreshRateInHighZone; + @GuardedBy("mLock") + private @Temperature.ThrottlingStatus int mThermalStatus = Temperature.THROTTLING_NONE; + BrightnessObserver(Context context, Handler handler, Injector injector) { mContext = context; mHandler = handler; @@ -1649,6 +1672,8 @@ public class DisplayModeDirector { R.integer.config_defaultRefreshRateInZone) : displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate(); } + mLowZoneRefreshRateForThermals = displayDeviceConfig == null ? null + : displayDeviceConfig.getLowBlockingZoneThermalMap(); mRefreshRateInLowZone = refreshRateInLowZone; } @@ -1668,6 +1693,8 @@ public class DisplayModeDirector { R.integer.config_fixedRefreshRateInHighZone) : displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate(); } + mHighZoneRefreshRateForThermals = displayDeviceConfig == null ? null + : displayDeviceConfig.getHighBlockingZoneThermalMap(); mRefreshRateInHighZone = refreshRateInHighZone; } @@ -2117,6 +2144,15 @@ public class DisplayModeDirector { if (insideLowZone) { refreshRateVote = Vote.forPhysicalRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone); + if (mLowZoneRefreshRateForThermals != null) { + RefreshRateRange range = SkinThermalStatusObserver + .findBestMatchingRefreshRateRange(mThermalStatus, + mLowZoneRefreshRateForThermals); + if (range != null) { + refreshRateVote = + Vote.forPhysicalRefreshRates(range.min, range.max); + } + } refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching(); } @@ -2126,6 +2162,15 @@ public class DisplayModeDirector { refreshRateVote = Vote.forPhysicalRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone); + if (mHighZoneRefreshRateForThermals != null) { + RefreshRateRange range = SkinThermalStatusObserver + .findBestMatchingRefreshRateRange(mThermalStatus, + mHighZoneRefreshRateForThermals); + if (range != null) { + refreshRateVote = + Vote.forPhysicalRefreshRates(range.min, range.max); + } + } refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching(); } @@ -2184,13 +2229,25 @@ public class DisplayModeDirector { + mRefreshRateChangeable); } + boolean registerForThermals = false; if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) { registerLightSensor(); - + registerForThermals = mLowZoneRefreshRateForThermals != null + || mHighZoneRefreshRateForThermals != null; } else { unregisterSensorListener(); } + + if (registerForThermals && !mThermalRegistered) { + mThermalRegistered = mInjector.registerThermalServiceListener(mThermalListener); + } else if (!registerForThermals && mThermalRegistered) { + mInjector.unregisterThermalServiceListener(mThermalListener); + mThermalRegistered = false; + synchronized (mLock) { + mThermalStatus = Temperature.THROTTLING_NONE; // reset + } + } } private void registerLightSensor() { @@ -2823,6 +2880,7 @@ public class DisplayModeDirector { boolean isDozeState(Display d); boolean registerThermalServiceListener(IThermalEventListener listener); + void unregisterThermalServiceListener(IThermalEventListener listener); boolean supportsFrameRateOverride(); } @@ -2918,6 +2976,19 @@ public class DisplayModeDirector { } @Override + public void unregisterThermalServiceListener(IThermalEventListener listener) { + IThermalService thermalService = getThermalService(); + if (thermalService == null) { + Slog.w(TAG, "Could not unregister thermal status. Service not available"); + } + try { + thermalService.unregisterThermalEventListener(listener); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to unregister thermal status listener", e); + } + } + + @Override public boolean supportsFrameRateOverride() { return SurfaceFlingerProperties.enable_frame_rate_override().orElse(true); } diff --git a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java index 58e15503be7d..b29cda88802a 100644 --- a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java +++ b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java @@ -64,6 +64,20 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme mHandler = handler; } + @Nullable + public static SurfaceControl.RefreshRateRange findBestMatchingRefreshRateRange( + @Temperature.ThrottlingStatus int currentStatus, + SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) { + SurfaceControl.RefreshRateRange foundRange = null; + for (int status = currentStatus; status >= 0; status--) { + foundRange = throttlingMap.get(status); + if (foundRange != null) { + break; + } + } + return foundRange; + } + void observe() { // if failed to register thermal service listener, don't register display listener if (!mInjector.registerThermalServiceListener(this)) { @@ -228,20 +242,6 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme } } - @Nullable - private SurfaceControl.RefreshRateRange findBestMatchingRefreshRateRange( - @Temperature.ThrottlingStatus int currentStatus, - SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) { - SurfaceControl.RefreshRateRange foundRange = null; - for (int status = currentStatus; status >= 0; status--) { - foundRange = throttlingMap.get(status); - if (foundRange != null) { - break; - } - } - return foundRange; - } - private void fallbackReportThrottlingIfNeeded(int displayId, @Temperature.ThrottlingStatus int currentStatus) { Vote vote = null; diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index 7104a80c668d..b63154d78a61 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -579,6 +579,10 @@ minOccurs="1" maxOccurs="1"> <xs:annotation name="final"/> </xs:element> + <xs:element type ="xs:string" name="refreshRateThermalThrottlingId"> + <xs:annotation name="nullable"/> + <xs:annotation name="final"/> + </xs:element> <xs:element name="blockingZoneThreshold" type="blockingZoneThreshold" minOccurs="1" maxOccurs="1"> <xs:annotation name="final"/> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 507c9dccda59..426fcb616213 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -17,8 +17,10 @@ package com.android.server.display.config { ctor public BlockingZoneConfig(); method public final com.android.server.display.config.BlockingZoneThreshold getBlockingZoneThreshold(); method public final java.math.BigInteger getDefaultRefreshRate(); + method @Nullable public final String getRefreshRateThermalThrottlingId(); method public final void setBlockingZoneThreshold(com.android.server.display.config.BlockingZoneThreshold); method public final void setDefaultRefreshRate(java.math.BigInteger); + method public final void setRefreshRateThermalThrottlingId(@Nullable String); } public class BlockingZoneThreshold { diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java index da7a6a10895a..4752f81a2b0f 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -268,6 +268,14 @@ public final class DisplayDeviceConfigTest { mDisplayDeviceConfig.getHdrBrightnessFromSdr(0.62f, 1.25f), SMALL_DELTA); + // Low/High zone thermal maps + assertEquals(new SurfaceControl.RefreshRateRange(30, 40), + mDisplayDeviceConfig.getLowBlockingZoneThermalMap() + .get(Temperature.THROTTLING_CRITICAL)); + assertEquals(new SurfaceControl.RefreshRateRange(40, 60), + mDisplayDeviceConfig.getHighBlockingZoneThermalMap() + .get(Temperature.THROTTLING_EMERGENCY)); + // Todo: Add asserts for DensityMapping, // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor. } @@ -530,6 +538,24 @@ public final class DisplayDeviceConfigTest { + " </refreshRateRange>\n" + " </refreshRateThrottlingPoint>\n" + "</refreshRateThrottlingMap>\n" + + "<refreshRateThrottlingMap id=\"thermalLow\">\n" + + " <refreshRateThrottlingPoint>\n" + + " <thermalStatus>critical</thermalStatus>\n" + + " <refreshRateRange>\n" + + " <minimum>30</minimum>\n" + + " <maximum>40</maximum>\n" + + " </refreshRateRange>\n" + + " </refreshRateThrottlingPoint>\n" + + "</refreshRateThrottlingMap>\n" + + "<refreshRateThrottlingMap id=\"thermalHigh\">\n" + + " <refreshRateThrottlingPoint>\n" + + " <thermalStatus>emergency</thermalStatus>\n" + + " <refreshRateRange>\n" + + " <minimum>40</minimum>\n" + + " <maximum>60</maximum>\n" + + " </refreshRateRange>\n" + + " </refreshRateThrottlingPoint>\n" + + "</refreshRateThrottlingMap>\n" + "<refreshRateThrottlingMap id=\"test\">\n" + " <refreshRateThrottlingPoint>\n" + " <thermalStatus>emergency</thermalStatus>\n" @@ -822,6 +848,8 @@ public final class DisplayDeviceConfigTest { + "<defaultRefreshRateInHbmSunlight>83</defaultRefreshRateInHbmSunlight>\n" + "<lowerBlockingZoneConfigs>\n" + "<defaultRefreshRate>75</defaultRefreshRate>\n" + + "<refreshRateThermalThrottlingId>thermalLow" + + "</refreshRateThermalThrottlingId>\n" + "<blockingZoneThreshold>\n" + "<displayBrightnessPoint>\n" + "<lux>50</lux>\n" @@ -843,6 +871,8 @@ public final class DisplayDeviceConfigTest { + "</lowerBlockingZoneConfigs>\n" + "<higherBlockingZoneConfigs>\n" + "<defaultRefreshRate>90</defaultRefreshRate>\n" + + "<refreshRateThermalThrottlingId>thermalHigh" + + "</refreshRateThermalThrottlingId>\n" + "<blockingZoneThreshold>\n" + "<displayBrightnessPoint>\n" + "<lux>70</lux>\n" diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index 15f13cdf9242..89a1e13636d8 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -39,6 +39,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -1100,6 +1101,196 @@ public class DisplayModeDirectorTest { } @Test + public void testLockFpsForHighZoneWithThermalCondition() throws Exception { + // First, configure brightness zones or DMD won't register for sensor data. + final FakeDeviceConfig config = mInjector.getDeviceConfig(); + config.setHighDisplayBrightnessThresholds(new int[] { 200 }); + config.setHighAmbientBrightnessThresholds(new int[] { 8000 }); + + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 120.f}, 0); + setPeakRefreshRate(120 /*fps*/); + director.getSettingsObserver().setDefaultRefreshRate(120); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + + // Set the thresholds for High Zone + DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class); + when(ddcMock.getDefaultHighBlockingZoneRefreshRate()).thenReturn(90); + when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[] { 200 }); + when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[] { 8000 }); + when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90); + when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[] {}); + when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[] {}); + + // Set the thermal condition for refresh rate range + when(ddcMock.getHighBlockingZoneThermalMap()).thenReturn( + new SparseArray<RefreshRateRange>() {{ + put(Temperature.THROTTLING_CRITICAL, new RefreshRateRange(60, 60)); + }} + ); + director.defaultDisplayDeviceUpdated(ddcMock); // set the ddc + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + director.start(sensorManager); + + // Get the display listener so that we can send it new brightness events + ArgumentCaptor<DisplayListener> displayListenerCaptor = + ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), + any(Handler.class), + eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED + | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS)); + DisplayListener displayListener = displayListenerCaptor.getValue(); + + // Get the sensor listener so that we can give it new light sensor events + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1))) + .registerListener( + listenerCaptor.capture(), + eq(lightSensor), + anyInt(), + any(Handler.class)); + SensorEventListener sensorListener = listenerCaptor.getValue(); + + // Get the thermal listener so that we can give it new thermal conditions + ArgumentCaptor<IThermalEventListener> thermalListenerCaptor = + ArgumentCaptor.forClass(IThermalEventListener.class); + verify(mInjector, atLeastOnce()).registerThermalServiceListener( + thermalListenerCaptor.capture()); + List<IThermalEventListener> thermalListeners = thermalListenerCaptor.getAllValues(); + + setBrightness(100, 100, displayListener); + // Sensor reads 2000 lux, + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000)); + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); + assertThat(vote).isNull(); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); + assertThat(vote).isNull(); + + // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this + // parameter to the necessary threshold + setBrightness(255, 255, displayListener); + // Sensor reads 9000 lux, + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000)); + + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); + assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); + assertThat(vote).isNotNull(); + assertThat(vote.disableRefreshRateSwitching).isTrue(); + + // Set critical and check new refresh rate + Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL); + for (var listener : thermalListeners) { + listener.notifyThrottling(temp); + } + + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); + assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); + assertThat(vote).isNotNull(); + assertThat(vote.disableRefreshRateSwitching).isTrue(); + } + + @Test + public void testLockFpsForLowZoneWithThermalCondition() throws Exception { + // First, configure brightness zones or DMD won't register for sensor data. + final FakeDeviceConfig config = mInjector.getDeviceConfig(); + config.setHighDisplayBrightnessThresholds(new int[] { 200 }); + config.setHighAmbientBrightnessThresholds(new int[] { 8000 }); + + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 120.f}, 0); + setPeakRefreshRate(120 /*fps*/); + director.getSettingsObserver().setDefaultRefreshRate(120); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + + // Set the thresholds for Low Zone + DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class); + when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90); + when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[] { 200 }); + when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[] { 8000 }); + when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90); + when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[] { 10 }); + when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[] { 10 }); + + // Set the thermal condition for refresh rate range + when(ddcMock.getLowBlockingZoneThermalMap()).thenReturn( + new SparseArray<RefreshRateRange>() {{ + put(Temperature.THROTTLING_CRITICAL, new RefreshRateRange(60, 60)); + }} + ); + director.defaultDisplayDeviceUpdated(ddcMock); // set the ddc + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + director.start(sensorManager); + + // Get the display listener so that we can send it new brightness events + ArgumentCaptor<DisplayListener> displayListenerCaptor = + ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), + any(Handler.class), + eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED + | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS)); + DisplayListener displayListener = displayListenerCaptor.getValue(); + + // Get the sensor listener so that we can give it new light sensor events + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1))) + .registerListener( + listenerCaptor.capture(), + eq(lightSensor), + anyInt(), + any(Handler.class)); + SensorEventListener sensorListener = listenerCaptor.getValue(); + + // Get the thermal listener so that we can give it new thermal conditions + ArgumentCaptor<IThermalEventListener> thermalListenerCaptor = + ArgumentCaptor.forClass(IThermalEventListener.class); + verify(mInjector, atLeastOnce()).registerThermalServiceListener( + thermalListenerCaptor.capture()); + List<IThermalEventListener> thermalListeners = thermalListenerCaptor.getAllValues(); + + setBrightness(100, 100, displayListener); + // Sensor reads 2000 lux, + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000)); + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); + assertThat(vote).isNull(); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); + assertThat(vote).isNull(); + + // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this + // parameter to the necessary threshold + setBrightness(5, 5, displayListener); + // Sensor reads 9 lux, + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9)); + + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); + assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); + assertThat(vote).isNotNull(); + assertThat(vote.disableRefreshRateSwitching).isTrue(); + + // Set critical and check new refresh rate + Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL); + for (var listener : thermalListeners) { + listener.notifyThrottling(temp); + } + + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); + assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); + assertThat(vote).isNotNull(); + assertThat(vote.disableRefreshRateSwitching).isTrue(); + } + + @Test public void testSensorRegistration() { // First, configure brightness zones or DMD won't register for sensor data. final FakeDeviceConfig config = mInjector.getDeviceConfig(); @@ -2902,6 +3093,10 @@ public class DisplayModeDirectorTest { } @Override + public void unregisterThermalServiceListener(IThermalEventListener listener) { + } + + @Override public boolean supportsFrameRateOverride() { return true; } |