Loading core/res/res/values/config.xml +15 −3 Original line number Diff line number Diff line Loading @@ -4165,9 +4165,21 @@ and a second time clipped to the fill level to indicate charge --> <bool name="config_batterymeterDualTone">false</bool> <!-- The default peak refresh rate for a given device. Change this value if you want to allow for higher refresh rates to be automatically used out of the box --> <integer name="config_defaultPeakRefreshRate">60</integer> <!-- The default refresh rate for a given device. Change this value to set a higher default refresh rate. If the hardware composer on the device supports display modes with a higher refresh rate than the default value specified here, the framework may use those higher refresh rate modes if an app chooses one by setting preferredDisplayModeId or calling setFrameRate(). If a non-zero value is set for config_defaultPeakRefreshRate, then config_defaultRefreshRate may be set to 0, in which case the value set for config_defaultPeakRefreshRate will act as the default frame rate. --> <integer name="config_defaultRefreshRate">60</integer> <!-- The default peak refresh rate for a given device. Change this value if you want to prevent the framework from using higher refresh rates, even if display modes with higher refresh rates are available from hardware composer. Only has an effect if the value is non-zero. --> <integer name="config_defaultPeakRefreshRate">0</integer> <!-- The display uses different gamma curves for different refresh rates. It's hard for panel vendor to tune the curves to have exact same brightness for different refresh rate. So Loading core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -3772,6 +3772,7 @@ <java-symbol type="string" name="bluetooth_airplane_mode_toast" /> <!-- For high refresh rate displays --> <java-symbol type="integer" name="config_defaultRefreshRate" /> <java-symbol type="integer" name="config_defaultPeakRefreshRate" /> <java-symbol type="integer" name="config_defaultRefreshRateInZone" /> <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" /> Loading services/core/java/com/android/server/display/DisplayModeDirector.java +69 −13 Original line number Diff line number Diff line Loading @@ -92,7 +92,7 @@ public class DisplayModeDirector { private final AppRequestObserver mAppRequestObserver; private final SettingsObserver mSettingsObserver; private final DisplayObserver mDisplayObserver; private final BrightnessObserver mBrightnessObserver; private BrightnessObserver mBrightnessObserver; private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener; Loading Loading @@ -460,6 +460,21 @@ public class DisplayModeDirector { mVotesByDisplay = votesByDisplay; } @VisibleForTesting void injectBrightnessObserver(BrightnessObserver brightnessObserver) { mBrightnessObserver = brightnessObserver; } @VisibleForTesting DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { synchronized (mLock) { mSettingsObserver.updateRefreshRateSettingLocked( minRefreshRate, peakRefreshRate, defaultRefreshRate); return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY); } } /** * Listens for changes refresh rate coordination. */ Loading Loading @@ -666,14 +681,18 @@ public class DisplayModeDirector { @VisibleForTesting static final class Vote { // DEFAULT_FRAME_RATE votes for [0, DEFAULT]. As the lowest priority vote, it's overridden // by all other considerations. It acts to set a default frame rate for a device. public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0; // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null. // If the higher voters result is a range, it will fix the rate to a single choice. // It's used to avoid rate switch in certain conditions. public static final int PRIORITY_LOW_BRIGHTNESS = 0; public static final int PRIORITY_LOW_BRIGHTNESS = 1; // 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 = 1; public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2; // We split the app request into different priorities in case we can satisfy one desire // without the other. Loading @@ -683,20 +702,20 @@ public class DisplayModeDirector { // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId // System also forces some apps like blacklisted app to run at a lower refresh rate. // @see android.R.array#config_highRefreshRateBlacklist public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 2; public static final int PRIORITY_APP_REQUEST_SIZE = 3; public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3; public static final int PRIORITY_APP_REQUEST_SIZE = 4; // 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 = 4; public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5; // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on. public static final int PRIORITY_LOW_POWER_MODE = 5; public static final int PRIORITY_LOW_POWER_MODE = 6; // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString. public static final int MIN_PRIORITY = PRIORITY_LOW_BRIGHTNESS; public static final int MIN_PRIORITY = PRIORITY_DEFAULT_REFRESH_RATE; public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE; // The cutoff for the app request refresh rate range. Votes with priorities lower than this Loading Loading @@ -740,6 +759,8 @@ public class DisplayModeDirector { public static String priorityToString(int priority) { switch (priority) { case PRIORITY_DEFAULT_REFRESH_RATE: return "PRIORITY_DEFAULT_REFRESH_RATE"; case PRIORITY_LOW_BRIGHTNESS: return "PRIORITY_LOW_BRIGHTNESS"; case PRIORITY_USER_SETTING_MIN_REFRESH_RATE: Loading Loading @@ -776,12 +797,15 @@ public class DisplayModeDirector { private final Context mContext; private float mDefaultPeakRefreshRate; private float mDefaultRefreshRate; SettingsObserver(@NonNull Context context, @NonNull Handler handler) { super(handler); mContext = context; mDefaultPeakRefreshRate = (float) context.getResources().getInteger( R.integer.config_defaultPeakRefreshRate); mDefaultRefreshRate = (float) context.getResources().getInteger(R.integer.config_defaultRefreshRate); } public void observe() { Loading Loading @@ -849,17 +873,48 @@ public class DisplayModeDirector { Settings.System.MIN_REFRESH_RATE, 0f); float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate); updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate))); updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate); } private void updateRefreshRateSettingLocked( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is // used to predict if we're going to be doing frequent refresh rate switching, and if // so, enable the brightness observer. The logic here is more complicated and fragile // than necessary, and we should improve it. See b/156304339 for more info. Vote peakVote = peakRefreshRate == 0f ? null : Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate)); updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, peakVote); updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY)); Vote defaultVote = defaultRefreshRate == 0f ? null : Vote.forRefreshRates(0f, defaultRefreshRate); updateVoteLocked(Vote.PRIORITY_DEFAULT_REFRESH_RATE, defaultVote); float maxRefreshRate; if (peakRefreshRate == 0f && defaultRefreshRate == 0f) { // We require that at least one of the peak or default refresh rate values are // set. The brightness observer requires that we're able to predict whether or not // we're going to do frequent refresh rate switching, and with the way the code is // currently written, we need either a default or peak refresh rate value for that. Slog.e(TAG, "Default and peak refresh rates are both 0. One of them should be set" + " to a valid value."); maxRefreshRate = minRefreshRate; } else if (peakRefreshRate == 0f) { maxRefreshRate = defaultRefreshRate; } else if (defaultRefreshRate == 0f) { maxRefreshRate = peakRefreshRate; } else { maxRefreshRate = Math.min(defaultRefreshRate, peakRefreshRate); } mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, peakRefreshRate); mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, maxRefreshRate); } public void dumpLocked(PrintWriter pw) { pw.println(" SettingsObserver"); pw.println(" mDefaultRefreshRate: " + mDefaultRefreshRate); pw.println(" mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate); } } Loading Loading @@ -1014,7 +1069,8 @@ public class DisplayModeDirector { * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}. */ private class BrightnessObserver extends ContentObserver { @VisibleForTesting public class BrightnessObserver extends ContentObserver { // TODO: brightnessfloat: change this to the float setting private final Uri mDisplayBrightnessSetting = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); Loading services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +81 −14 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.display; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verify; import android.content.Context; import android.os.Handler; Loading @@ -28,6 +29,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.display.DisplayModeDirector.BrightnessObserver; import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs; import com.android.server.display.DisplayModeDirector.Vote; Loading @@ -36,6 +38,7 @@ import com.google.common.truth.Truth; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest Loading @@ -52,16 +55,15 @@ public class DisplayModeDirectorTest { mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); } private DisplayModeDirector createDisplayModeDirectorWithDisplayFpsRange( int minFps, int maxFps) { private DisplayModeDirector createDirectorFromRefreshRateArray( float[] refreshRates, int baseModeId) { DisplayModeDirector director = new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper())); int displayId = 0; int numModes = maxFps - minFps + 1; Display.Mode[] modes = new Display.Mode[numModes]; for (int i = minFps; i <= maxFps; i++) { modes[i - minFps] = new Display.Mode( /*modeId=*/i, /*width=*/1000, /*height=*/1000, /*refreshRate=*/i); Display.Mode[] modes = new Display.Mode[refreshRates.length]; for (int i = 0; i < refreshRates.length; i++) { modes[i] = new Display.Mode( /*modeId=*/baseModeId + i, /*width=*/1000, /*height=*/1000, refreshRates[i]); } SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>(); supportedModesByDisplay.put(displayId, modes); Loading @@ -72,14 +74,22 @@ public class DisplayModeDirectorTest { return director; } private DisplayModeDirector createDirectorFromFpsRange(int minFps, int maxFps) { int numRefreshRates = maxFps - minFps + 1; float[] refreshRates = new float[numRefreshRates]; for (int i = 0; i < numRefreshRates; i++) { refreshRates[i] = minFps + i; } return createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/minFps); } @Test public void testDisplayModeVoting() { int displayId = 0; // With no votes present, DisplayModeDirector should allow any refresh rate. DesiredDisplayModeSpecs modeSpecs = createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayModeSpecs( displayId); createDirectorFromFpsRange(60, 90).getDesiredDisplayModeSpecs(displayId); Truth.assertThat(modeSpecs.baseModeId).isEqualTo(60); Truth.assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(0f); Truth.assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(Float.POSITIVE_INFINITY); Loading @@ -92,7 +102,7 @@ public class DisplayModeDirectorTest { { int minFps = 60; int maxFps = 90; DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); assertTrue(2 * numPriorities < maxFps - minFps + 1); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); Loading @@ -114,7 +124,7 @@ public class DisplayModeDirectorTest { // presence of higher priority votes. { assertTrue(numPriorities >= 2); DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); Loading @@ -131,7 +141,7 @@ public class DisplayModeDirectorTest { @Test public void testVotingWithFloatingPointErrors() { int displayId = 0; DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(50, 90); DisplayModeDirector director = createDirectorFromFpsRange(50, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); Loading @@ -154,7 +164,7 @@ public class DisplayModeDirectorTest { assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE); int displayId = 0; DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); Loading Loading @@ -202,7 +212,7 @@ public class DisplayModeDirectorTest { >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); int displayId = 0; DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); Loading Loading @@ -235,4 +245,61 @@ public class DisplayModeDirectorTest { .isWithin(FLOAT_TOLERANCE) .of(75); } void verifySpecsWithRefreshRateSettings(DisplayModeDirector director, float minFps, float peakFps, float defaultFps, float primaryMin, float primaryMax, float appRequestMin, float appRequestMax) { DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecsWithInjectedFpsSettings( minFps, peakFps, defaultFps); Truth.assertThat(specs.primaryRefreshRateRange.min).isEqualTo(primaryMin); Truth.assertThat(specs.primaryRefreshRateRange.max).isEqualTo(primaryMax); Truth.assertThat(specs.appRequestRefreshRateRange.min).isEqualTo(appRequestMin); Truth.assertThat(specs.appRequestRefreshRateRange.max).isEqualTo(appRequestMax); } @Test public void testSpecsFromRefreshRateSettings() { // Confirm that, with varying settings for min, peak, and default refresh rate, // DesiredDisplayModeSpecs is calculated correctly. float[] refreshRates = {30.f, 60.f, 90.f, 120.f, 150.f}; DisplayModeDirector director = createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/0); float inf = Float.POSITIVE_INFINITY; verifySpecsWithRefreshRateSettings(director, 0, 0, 0, 0, inf, 0, inf); verifySpecsWithRefreshRateSettings(director, 0, 0, 90, 0, 90, 0, inf); verifySpecsWithRefreshRateSettings(director, 0, 90, 0, 0, 90, 0, 90); verifySpecsWithRefreshRateSettings(director, 0, 90, 60, 0, 60, 0, 90); verifySpecsWithRefreshRateSettings(director, 0, 90, 120, 0, 90, 0, 90); verifySpecsWithRefreshRateSettings(director, 90, 0, 0, 90, inf, 0, inf); verifySpecsWithRefreshRateSettings(director, 90, 0, 120, 90, 120, 0, inf); verifySpecsWithRefreshRateSettings(director, 90, 0, 60, 90, inf, 0, inf); verifySpecsWithRefreshRateSettings(director, 90, 120, 0, 90, 120, 0, 120); verifySpecsWithRefreshRateSettings(director, 90, 60, 0, 90, 90, 0, 90); verifySpecsWithRefreshRateSettings(director, 60, 120, 90, 60, 90, 0, 120); } void verifyBrightnessObserverCall(DisplayModeDirector director, float minFps, float peakFps, float defaultFps, float brightnessObserverMin, float brightnessObserverMax) { BrightnessObserver brightnessObserver = Mockito.mock(BrightnessObserver.class); director.injectBrightnessObserver(brightnessObserver); director.getDesiredDisplayModeSpecsWithInjectedFpsSettings(minFps, peakFps, defaultFps); verify(brightnessObserver) .onRefreshRateSettingChangedLocked(brightnessObserverMin, brightnessObserverMax); } @Test public void testBrightnessObserverCallWithRefreshRateSettings() { // Confirm that, with varying settings for min, peak, and default refresh rate, we make the // correct call to the brightness observer. float[] refreshRates = {60.f, 90.f, 120.f}; DisplayModeDirector director = createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/0); verifyBrightnessObserverCall(director, 0, 0, 0, 0, 0); verifyBrightnessObserverCall(director, 0, 0, 90, 0, 90); verifyBrightnessObserverCall(director, 0, 90, 0, 0, 90); verifyBrightnessObserverCall(director, 0, 90, 60, 0, 60); verifyBrightnessObserverCall(director, 90, 90, 0, 90, 90); verifyBrightnessObserverCall(director, 120, 90, 0, 120, 90); } } Loading
core/res/res/values/config.xml +15 −3 Original line number Diff line number Diff line Loading @@ -4165,9 +4165,21 @@ and a second time clipped to the fill level to indicate charge --> <bool name="config_batterymeterDualTone">false</bool> <!-- The default peak refresh rate for a given device. Change this value if you want to allow for higher refresh rates to be automatically used out of the box --> <integer name="config_defaultPeakRefreshRate">60</integer> <!-- The default refresh rate for a given device. Change this value to set a higher default refresh rate. If the hardware composer on the device supports display modes with a higher refresh rate than the default value specified here, the framework may use those higher refresh rate modes if an app chooses one by setting preferredDisplayModeId or calling setFrameRate(). If a non-zero value is set for config_defaultPeakRefreshRate, then config_defaultRefreshRate may be set to 0, in which case the value set for config_defaultPeakRefreshRate will act as the default frame rate. --> <integer name="config_defaultRefreshRate">60</integer> <!-- The default peak refresh rate for a given device. Change this value if you want to prevent the framework from using higher refresh rates, even if display modes with higher refresh rates are available from hardware composer. Only has an effect if the value is non-zero. --> <integer name="config_defaultPeakRefreshRate">0</integer> <!-- The display uses different gamma curves for different refresh rates. It's hard for panel vendor to tune the curves to have exact same brightness for different refresh rate. So Loading
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -3772,6 +3772,7 @@ <java-symbol type="string" name="bluetooth_airplane_mode_toast" /> <!-- For high refresh rate displays --> <java-symbol type="integer" name="config_defaultRefreshRate" /> <java-symbol type="integer" name="config_defaultPeakRefreshRate" /> <java-symbol type="integer" name="config_defaultRefreshRateInZone" /> <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" /> Loading
services/core/java/com/android/server/display/DisplayModeDirector.java +69 −13 Original line number Diff line number Diff line Loading @@ -92,7 +92,7 @@ public class DisplayModeDirector { private final AppRequestObserver mAppRequestObserver; private final SettingsObserver mSettingsObserver; private final DisplayObserver mDisplayObserver; private final BrightnessObserver mBrightnessObserver; private BrightnessObserver mBrightnessObserver; private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener; Loading Loading @@ -460,6 +460,21 @@ public class DisplayModeDirector { mVotesByDisplay = votesByDisplay; } @VisibleForTesting void injectBrightnessObserver(BrightnessObserver brightnessObserver) { mBrightnessObserver = brightnessObserver; } @VisibleForTesting DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { synchronized (mLock) { mSettingsObserver.updateRefreshRateSettingLocked( minRefreshRate, peakRefreshRate, defaultRefreshRate); return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY); } } /** * Listens for changes refresh rate coordination. */ Loading Loading @@ -666,14 +681,18 @@ public class DisplayModeDirector { @VisibleForTesting static final class Vote { // DEFAULT_FRAME_RATE votes for [0, DEFAULT]. As the lowest priority vote, it's overridden // by all other considerations. It acts to set a default frame rate for a device. public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0; // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null. // If the higher voters result is a range, it will fix the rate to a single choice. // It's used to avoid rate switch in certain conditions. public static final int PRIORITY_LOW_BRIGHTNESS = 0; public static final int PRIORITY_LOW_BRIGHTNESS = 1; // 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 = 1; public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2; // We split the app request into different priorities in case we can satisfy one desire // without the other. Loading @@ -683,20 +702,20 @@ public class DisplayModeDirector { // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId // System also forces some apps like blacklisted app to run at a lower refresh rate. // @see android.R.array#config_highRefreshRateBlacklist public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 2; public static final int PRIORITY_APP_REQUEST_SIZE = 3; public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3; public static final int PRIORITY_APP_REQUEST_SIZE = 4; // 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 = 4; public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5; // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on. public static final int PRIORITY_LOW_POWER_MODE = 5; public static final int PRIORITY_LOW_POWER_MODE = 6; // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString. public static final int MIN_PRIORITY = PRIORITY_LOW_BRIGHTNESS; public static final int MIN_PRIORITY = PRIORITY_DEFAULT_REFRESH_RATE; public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE; // The cutoff for the app request refresh rate range. Votes with priorities lower than this Loading Loading @@ -740,6 +759,8 @@ public class DisplayModeDirector { public static String priorityToString(int priority) { switch (priority) { case PRIORITY_DEFAULT_REFRESH_RATE: return "PRIORITY_DEFAULT_REFRESH_RATE"; case PRIORITY_LOW_BRIGHTNESS: return "PRIORITY_LOW_BRIGHTNESS"; case PRIORITY_USER_SETTING_MIN_REFRESH_RATE: Loading Loading @@ -776,12 +797,15 @@ public class DisplayModeDirector { private final Context mContext; private float mDefaultPeakRefreshRate; private float mDefaultRefreshRate; SettingsObserver(@NonNull Context context, @NonNull Handler handler) { super(handler); mContext = context; mDefaultPeakRefreshRate = (float) context.getResources().getInteger( R.integer.config_defaultPeakRefreshRate); mDefaultRefreshRate = (float) context.getResources().getInteger(R.integer.config_defaultRefreshRate); } public void observe() { Loading Loading @@ -849,17 +873,48 @@ public class DisplayModeDirector { Settings.System.MIN_REFRESH_RATE, 0f); float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate); updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate))); updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate); } private void updateRefreshRateSettingLocked( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is // used to predict if we're going to be doing frequent refresh rate switching, and if // so, enable the brightness observer. The logic here is more complicated and fragile // than necessary, and we should improve it. See b/156304339 for more info. Vote peakVote = peakRefreshRate == 0f ? null : Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate)); updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, peakVote); updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY)); Vote defaultVote = defaultRefreshRate == 0f ? null : Vote.forRefreshRates(0f, defaultRefreshRate); updateVoteLocked(Vote.PRIORITY_DEFAULT_REFRESH_RATE, defaultVote); float maxRefreshRate; if (peakRefreshRate == 0f && defaultRefreshRate == 0f) { // We require that at least one of the peak or default refresh rate values are // set. The brightness observer requires that we're able to predict whether or not // we're going to do frequent refresh rate switching, and with the way the code is // currently written, we need either a default or peak refresh rate value for that. Slog.e(TAG, "Default and peak refresh rates are both 0. One of them should be set" + " to a valid value."); maxRefreshRate = minRefreshRate; } else if (peakRefreshRate == 0f) { maxRefreshRate = defaultRefreshRate; } else if (defaultRefreshRate == 0f) { maxRefreshRate = peakRefreshRate; } else { maxRefreshRate = Math.min(defaultRefreshRate, peakRefreshRate); } mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, peakRefreshRate); mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, maxRefreshRate); } public void dumpLocked(PrintWriter pw) { pw.println(" SettingsObserver"); pw.println(" mDefaultRefreshRate: " + mDefaultRefreshRate); pw.println(" mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate); } } Loading Loading @@ -1014,7 +1069,8 @@ public class DisplayModeDirector { * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}. */ private class BrightnessObserver extends ContentObserver { @VisibleForTesting public class BrightnessObserver extends ContentObserver { // TODO: brightnessfloat: change this to the float setting private final Uri mDisplayBrightnessSetting = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); Loading
services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +81 −14 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.display; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verify; import android.content.Context; import android.os.Handler; Loading @@ -28,6 +29,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.display.DisplayModeDirector.BrightnessObserver; import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs; import com.android.server.display.DisplayModeDirector.Vote; Loading @@ -36,6 +38,7 @@ import com.google.common.truth.Truth; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest Loading @@ -52,16 +55,15 @@ public class DisplayModeDirectorTest { mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); } private DisplayModeDirector createDisplayModeDirectorWithDisplayFpsRange( int minFps, int maxFps) { private DisplayModeDirector createDirectorFromRefreshRateArray( float[] refreshRates, int baseModeId) { DisplayModeDirector director = new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper())); int displayId = 0; int numModes = maxFps - minFps + 1; Display.Mode[] modes = new Display.Mode[numModes]; for (int i = minFps; i <= maxFps; i++) { modes[i - minFps] = new Display.Mode( /*modeId=*/i, /*width=*/1000, /*height=*/1000, /*refreshRate=*/i); Display.Mode[] modes = new Display.Mode[refreshRates.length]; for (int i = 0; i < refreshRates.length; i++) { modes[i] = new Display.Mode( /*modeId=*/baseModeId + i, /*width=*/1000, /*height=*/1000, refreshRates[i]); } SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>(); supportedModesByDisplay.put(displayId, modes); Loading @@ -72,14 +74,22 @@ public class DisplayModeDirectorTest { return director; } private DisplayModeDirector createDirectorFromFpsRange(int minFps, int maxFps) { int numRefreshRates = maxFps - minFps + 1; float[] refreshRates = new float[numRefreshRates]; for (int i = 0; i < numRefreshRates; i++) { refreshRates[i] = minFps + i; } return createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/minFps); } @Test public void testDisplayModeVoting() { int displayId = 0; // With no votes present, DisplayModeDirector should allow any refresh rate. DesiredDisplayModeSpecs modeSpecs = createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayModeSpecs( displayId); createDirectorFromFpsRange(60, 90).getDesiredDisplayModeSpecs(displayId); Truth.assertThat(modeSpecs.baseModeId).isEqualTo(60); Truth.assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(0f); Truth.assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(Float.POSITIVE_INFINITY); Loading @@ -92,7 +102,7 @@ public class DisplayModeDirectorTest { { int minFps = 60; int maxFps = 90; DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); assertTrue(2 * numPriorities < maxFps - minFps + 1); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); Loading @@ -114,7 +124,7 @@ public class DisplayModeDirectorTest { // presence of higher priority votes. { assertTrue(numPriorities >= 2); DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); Loading @@ -131,7 +141,7 @@ public class DisplayModeDirectorTest { @Test public void testVotingWithFloatingPointErrors() { int displayId = 0; DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(50, 90); DisplayModeDirector director = createDirectorFromFpsRange(50, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); Loading @@ -154,7 +164,7 @@ public class DisplayModeDirectorTest { assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE); int displayId = 0; DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); Loading Loading @@ -202,7 +212,7 @@ public class DisplayModeDirectorTest { >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); int displayId = 0; DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); Loading Loading @@ -235,4 +245,61 @@ public class DisplayModeDirectorTest { .isWithin(FLOAT_TOLERANCE) .of(75); } void verifySpecsWithRefreshRateSettings(DisplayModeDirector director, float minFps, float peakFps, float defaultFps, float primaryMin, float primaryMax, float appRequestMin, float appRequestMax) { DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecsWithInjectedFpsSettings( minFps, peakFps, defaultFps); Truth.assertThat(specs.primaryRefreshRateRange.min).isEqualTo(primaryMin); Truth.assertThat(specs.primaryRefreshRateRange.max).isEqualTo(primaryMax); Truth.assertThat(specs.appRequestRefreshRateRange.min).isEqualTo(appRequestMin); Truth.assertThat(specs.appRequestRefreshRateRange.max).isEqualTo(appRequestMax); } @Test public void testSpecsFromRefreshRateSettings() { // Confirm that, with varying settings for min, peak, and default refresh rate, // DesiredDisplayModeSpecs is calculated correctly. float[] refreshRates = {30.f, 60.f, 90.f, 120.f, 150.f}; DisplayModeDirector director = createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/0); float inf = Float.POSITIVE_INFINITY; verifySpecsWithRefreshRateSettings(director, 0, 0, 0, 0, inf, 0, inf); verifySpecsWithRefreshRateSettings(director, 0, 0, 90, 0, 90, 0, inf); verifySpecsWithRefreshRateSettings(director, 0, 90, 0, 0, 90, 0, 90); verifySpecsWithRefreshRateSettings(director, 0, 90, 60, 0, 60, 0, 90); verifySpecsWithRefreshRateSettings(director, 0, 90, 120, 0, 90, 0, 90); verifySpecsWithRefreshRateSettings(director, 90, 0, 0, 90, inf, 0, inf); verifySpecsWithRefreshRateSettings(director, 90, 0, 120, 90, 120, 0, inf); verifySpecsWithRefreshRateSettings(director, 90, 0, 60, 90, inf, 0, inf); verifySpecsWithRefreshRateSettings(director, 90, 120, 0, 90, 120, 0, 120); verifySpecsWithRefreshRateSettings(director, 90, 60, 0, 90, 90, 0, 90); verifySpecsWithRefreshRateSettings(director, 60, 120, 90, 60, 90, 0, 120); } void verifyBrightnessObserverCall(DisplayModeDirector director, float minFps, float peakFps, float defaultFps, float brightnessObserverMin, float brightnessObserverMax) { BrightnessObserver brightnessObserver = Mockito.mock(BrightnessObserver.class); director.injectBrightnessObserver(brightnessObserver); director.getDesiredDisplayModeSpecsWithInjectedFpsSettings(minFps, peakFps, defaultFps); verify(brightnessObserver) .onRefreshRateSettingChangedLocked(brightnessObserverMin, brightnessObserverMax); } @Test public void testBrightnessObserverCallWithRefreshRateSettings() { // Confirm that, with varying settings for min, peak, and default refresh rate, we make the // correct call to the brightness observer. float[] refreshRates = {60.f, 90.f, 120.f}; DisplayModeDirector director = createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/0); verifyBrightnessObserverCall(director, 0, 0, 0, 0, 0); verifyBrightnessObserverCall(director, 0, 0, 90, 0, 90); verifyBrightnessObserverCall(director, 0, 90, 0, 0, 90); verifyBrightnessObserverCall(director, 0, 90, 60, 0, 60); verifyBrightnessObserverCall(director, 90, 90, 0, 90, 90); verifyBrightnessObserverCall(director, 120, 90, 0, 120, 90); } }