Add thermal options for refresh rate blocking zones.
Bug: 294852396
Test: atest DisplayModeDirectorTest
Change-Id: Iad1d3b7786a019f68dea3d0820fe587cb36c8a1d
Merged-In: Iad1d3b7786a019f68dea3d0820fe587cb36c8a1d
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index d647757..6a36fbe 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 @@
}
/**
- * @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 e5965ef..cdcc1c8 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 @@
* <defaultRefreshRateInHbmSunlight>75</defaultRefreshRateInHbmSunlight>
* <lowerBlockingZoneConfigs>
* <defaultRefreshRate>75</defaultRefreshRate>
+ * <refreshRateThermalThrottlingId>id_of_a_throttling_map</refreshRateThermalThrottlingId>
* <blockingZoneThreshold>
* <displayBrightnessPoint>
* <lux>50</lux>
@@ -722,6 +723,12 @@
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 @@
}
/**
+ * @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 @@
}
/**
+ * @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 @@
+ ", mDefaultRefreshRateInHbmHdr= " + mDefaultRefreshRateInHbmHdr
+ ", mDefaultRefreshRateInHbmSunlight= " + mDefaultRefreshRateInHbmSunlight
+ ", mRefreshRateThrottlingMap= " + mRefreshRateThrottlingMap
+ + ", mLowBlockingZoneThermalMapId= " + mLowBlockingZoneThermalMapId
+ + ", mHighBlockingZoneThermalMapId= " + mHighBlockingZoneThermalMapId
+ "\n"
+ ", mLowDisplayBrightnessThresholds= "
+ Arrays.toString(mLowDisplayBrightnessThresholds)
@@ -2114,9 +2137,13 @@
}
/**
- * 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 @@
* 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 82755b6..6079a32 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 @@
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 @@
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 @@
R.integer.config_defaultRefreshRateInZone)
: displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate();
}
+ mLowZoneRefreshRateForThermals = displayDeviceConfig == null ? null
+ : displayDeviceConfig.getLowBlockingZoneThermalMap();
mRefreshRateInLowZone = refreshRateInLowZone;
}
@@ -1668,6 +1693,8 @@
R.integer.config_fixedRefreshRateInHighZone)
: displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate();
}
+ mHighZoneRefreshRateForThermals = displayDeviceConfig == null ? null
+ : displayDeviceConfig.getHighBlockingZoneThermalMap();
mRefreshRateInHighZone = refreshRateInHighZone;
}
@@ -2117,6 +2144,15 @@
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 @@
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 @@
+ 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 @@
boolean isDozeState(Display d);
boolean registerThermalServiceListener(IThermalEventListener listener);
+ void unregisterThermalServiceListener(IThermalEventListener listener);
boolean supportsFrameRateOverride();
}
@@ -2918,6 +2976,19 @@
}
@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 58e1550..b29cda8 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 @@
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 @@
}
}
- @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 7104a80..b63154d 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 507c9dc..426fcb6 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 @@
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 da7a6a1..4752f81 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 @@
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 @@
+ " </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 @@
+ "<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 @@
+ "</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 15f13cd..89a1e13 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.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 @@
}
@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 @@
}
@Override
+ public void unregisterThermalServiceListener(IThermalEventListener listener) {
+ }
+
+ @Override
public boolean supportsFrameRateOverride() {
return true;
}