diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 912b1b2e8da23..c131ed62c6b2b 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -523,8 +523,10 @@ public class DisplayModeDirector { * changed */ public void defaultDisplayDeviceUpdated(DisplayDeviceConfig displayDeviceConfig) { - mSettingsObserver.setRefreshRates(displayDeviceConfig); - mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig); + mSettingsObserver.setRefreshRates(displayDeviceConfig, + /* attemptLoadingFromDeviceConfig= */ true); + mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig, + /* attemptLoadingFromDeviceConfig= */ true); } /** @@ -1142,19 +1144,25 @@ public class DisplayModeDirector { SettingsObserver(@NonNull Context context, @NonNull Handler handler) { super(handler); mContext = context; - setRefreshRates(/* displayDeviceConfig= */ null); + // We don't want to load from the DeviceConfig while constructing since this leads to + // a spike in the latency of DisplayManagerService startup. This happens because + // reading from the DeviceConfig is an intensive IO operation and having it in the + // startup phase where we thrive to keep the latency very low has significant impact. + setRefreshRates(/* displayDeviceConfig= */ null, + /* attemptLoadingFromDeviceConfig= */ false); } /** * This is used to update the refresh rate configs from the DeviceConfig, which * if missing from DisplayDeviceConfig, and finally fallback to config.xml. */ - public void setRefreshRates(DisplayDeviceConfig displayDeviceConfig) { - setDefaultPeakRefreshRate(displayDeviceConfig); + public void setRefreshRates(DisplayDeviceConfig displayDeviceConfig, + boolean attemptLoadingFromDeviceConfig) { + setDefaultPeakRefreshRate(displayDeviceConfig, attemptLoadingFromDeviceConfig); mDefaultRefreshRate = (displayDeviceConfig == null) ? (float) mContext.getResources().getInteger( - R.integer.config_defaultRefreshRate) - : (float) displayDeviceConfig.getDefaultRefreshRate(); + R.integer.config_defaultRefreshRate) + : (float) displayDeviceConfig.getDefaultRefreshRate(); } public void observe() { @@ -1215,13 +1223,27 @@ public class DisplayModeDirector { } } - private void setDefaultPeakRefreshRate(DisplayDeviceConfig displayDeviceConfig) { + @VisibleForTesting + float getDefaultRefreshRate() { + return mDefaultRefreshRate; + } + + @VisibleForTesting + float getDefaultPeakRefreshRate() { + return mDefaultPeakRefreshRate; + } + + private void setDefaultPeakRefreshRate(DisplayDeviceConfig displayDeviceConfig, + boolean attemptLoadingFromDeviceConfig) { Float defaultPeakRefreshRate = null; - try { - defaultPeakRefreshRate = + + if (attemptLoadingFromDeviceConfig) { + try { + defaultPeakRefreshRate = mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate(); - } catch (Exception exception) { - // Do nothing + } catch (Exception exception) { + // Do nothing + } } if (defaultPeakRefreshRate == null) { defaultPeakRefreshRate = @@ -1544,7 +1566,8 @@ public class DisplayModeDirector { mContext = context; mHandler = handler; mInjector = injector; - updateBlockingZoneThresholds(/* displayDeviceConfig= */ null); + updateBlockingZoneThresholds(/* displayDeviceConfig= */ null, + /* attemptLoadingFromDeviceConfig= */ false); mRefreshRateInHighZone = context.getResources().getInteger( R.integer.config_fixedRefreshRateInHighZone); } @@ -1553,22 +1576,44 @@ public class DisplayModeDirector { * This is used to update the blocking zone thresholds from the DeviceConfig, which * if missing from DisplayDeviceConfig, and finally fallback to config.xml. */ - public void updateBlockingZoneThresholds(DisplayDeviceConfig displayDeviceConfig) { - loadLowBrightnessThresholds(displayDeviceConfig); - loadHighBrightnessThresholds(displayDeviceConfig); + public void updateBlockingZoneThresholds(DisplayDeviceConfig displayDeviceConfig, + boolean attemptLoadingFromDeviceConfig) { + loadLowBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig); + loadHighBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig); } - private void loadLowBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig) { + @VisibleForTesting + int[] getLowDisplayBrightnessThreshold() { + return mLowDisplayBrightnessThresholds; + } + + @VisibleForTesting + int[] getLowAmbientBrightnessThreshold() { + return mLowAmbientBrightnessThresholds; + } + + @VisibleForTesting + int[] getHighDisplayBrightnessThreshold() { + return mHighDisplayBrightnessThresholds; + } + + @VisibleForTesting + int[] getHighAmbientBrightnessThreshold() { + return mHighAmbientBrightnessThresholds; + } + + private void loadLowBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig, + boolean attemptLoadingFromDeviceConfig) { mLowDisplayBrightnessThresholds = loadBrightnessThresholds( () -> mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(), () -> displayDeviceConfig.getLowDisplayBrightnessThresholds(), R.array.config_brightnessThresholdsOfPeakRefreshRate, - displayDeviceConfig); + displayDeviceConfig, attemptLoadingFromDeviceConfig); mLowAmbientBrightnessThresholds = loadBrightnessThresholds( () -> mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(), () -> displayDeviceConfig.getLowAmbientBrightnessThresholds(), R.array.config_ambientThresholdsOfPeakRefreshRate, - displayDeviceConfig); + displayDeviceConfig, attemptLoadingFromDeviceConfig); if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) { throw new RuntimeException("display low brightness threshold array and ambient " + "brightness threshold array have different length: " @@ -1579,17 +1624,18 @@ public class DisplayModeDirector { } } - private void loadHighBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig) { + private void loadHighBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig, + boolean attemptLoadingFromDeviceConfig) { mHighDisplayBrightnessThresholds = loadBrightnessThresholds( () -> mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(), () -> displayDeviceConfig.getHighDisplayBrightnessThresholds(), R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate, - displayDeviceConfig); + displayDeviceConfig, attemptLoadingFromDeviceConfig); mHighAmbientBrightnessThresholds = loadBrightnessThresholds( () -> mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(), () -> displayDeviceConfig.getHighAmbientBrightnessThresholds(), R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate, - displayDeviceConfig); + displayDeviceConfig, attemptLoadingFromDeviceConfig); if (mHighDisplayBrightnessThresholds.length != mHighAmbientBrightnessThresholds.length) { throw new RuntimeException("display high brightness threshold array and ambient " @@ -1605,13 +1651,16 @@ public class DisplayModeDirector { Callable loadFromDeviceConfigDisplaySettingsCallable, Callable loadFromDisplayDeviceConfigCallable, int brightnessThresholdOfFixedRefreshRateKey, - DisplayDeviceConfig displayDeviceConfig) { + DisplayDeviceConfig displayDeviceConfig, boolean attemptLoadingFromDeviceConfig) { int[] brightnessThresholds = null; - try { - brightnessThresholds = + + if (attemptLoadingFromDeviceConfig) { + try { + brightnessThresholds = loadFromDeviceConfigDisplaySettingsCallable.call(); - } catch (Exception exception) { - // Do nothing + } catch (Exception exception) { + // Do nothing + } } if (brightnessThresholds == null) { try { 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 18dd264558ac4..fb0cdfa9a680c 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -21,6 +21,7 @@ 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_PEAK_REFRESH_RATE_DEFAULT; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE; @@ -31,6 +32,8 @@ import static com.android.server.display.HighBrightnessModeController.HBM_TRANSI import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -48,6 +51,7 @@ import android.annotation.NonNull; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; +import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.Sensor; import android.hardware.SensorEventListener; @@ -76,6 +80,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.R; import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.util.Preconditions; import com.android.internal.util.test.FakeSettingsProvider; @@ -1855,16 +1860,83 @@ public class DisplayModeDirectorTest { @Test public void testNotifyDefaultDisplayDeviceUpdated() { - DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class); - when(displayDeviceConfig.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{}); - when(displayDeviceConfig.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{}); - when(displayDeviceConfig.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{}); - when(displayDeviceConfig.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{}); + Resources resources = mock(Resources.class); + when(mContext.getResources()).thenReturn(resources); + when(resources.getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate)) + .thenReturn(75); + when(resources.getInteger(R.integer.config_defaultRefreshRate)) + .thenReturn(45); + when(resources.getIntArray(R.array.config_brightnessThresholdsOfPeakRefreshRate)) + .thenReturn(new int[]{5}); + when(resources.getIntArray(R.array.config_ambientThresholdsOfPeakRefreshRate)) + .thenReturn(new int[]{10}); + when( + resources.getIntArray(R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate)) + .thenReturn(new int[]{250}); + when( + resources.getIntArray(R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate)) + .thenReturn(new int[]{7000}); DisplayModeDirector director = createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0); + // We don't expect any interaction with DeviceConfig when the director is initialized + // because we explicitly avoid doing this as this can lead to a latency spike in the + // startup of DisplayManagerService + // Verify all the loaded values are from DisplayDeviceConfig + assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 45, 0.0); + assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 75, + 0.0); + assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(), + new int[]{250}); + assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(), + new int[]{7000}); + assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(), + new int[]{5}); + assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(), + new int[]{10}); + + // Notify that the default display is updated, such that DisplayDeviceConfig has new values + DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class); + when(displayDeviceConfig.getDefaultRefreshRate()).thenReturn(50); + when(displayDeviceConfig.getDefaultPeakRefreshRate()).thenReturn(55); + when(displayDeviceConfig.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{25}); + when(displayDeviceConfig.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{30}); + when(displayDeviceConfig.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{210}); + when(displayDeviceConfig.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{2100}); director.defaultDisplayDeviceUpdated(displayDeviceConfig); - verify(displayDeviceConfig).getDefaultRefreshRate(); - verify(displayDeviceConfig).getDefaultPeakRefreshRate(); + + assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 50, 0.0); + assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 55, + 0.0); + assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(), + new int[]{210}); + assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(), + new int[]{2100}); + assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(), + new int[]{25}); + assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(), + new int[]{30}); + + // Notify that the default display is updated, such that DeviceConfig has new values + FakeDeviceConfig config = mInjector.getDeviceConfig(); + config.setDefaultPeakRefreshRate(60); + config.setLowAmbientBrightnessThresholds(new int[]{20}); + config.setLowDisplayBrightnessThresholds(new int[]{10}); + config.setHighDisplayBrightnessThresholds(new int[]{255}); + config.setHighAmbientBrightnessThresholds(new int[]{8000}); + + director.defaultDisplayDeviceUpdated(displayDeviceConfig); + + assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 50, 0.0); + assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 60, + 0.0); + assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(), + new int[]{255}); + assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(), + new int[]{8000}); + assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(), + new int[]{10}); + assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(), + new int[]{20}); } private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) { @@ -1954,6 +2026,12 @@ public class DisplayModeDirectorTest { String.valueOf(fps)); } + void setDefaultPeakRefreshRate(int fps) { + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_PEAK_REFRESH_RATE_DEFAULT, + String.valueOf(fps)); + } + void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) { String thresholds = toPropertyValue(brightnessThresholds);