diff options
4 files changed, 166 insertions, 30 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 003c0da95f9b..b62bb33256dc 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -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 diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9f3ace54c264..42e72609304f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3773,6 +3773,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" /> diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index c54ebf87e558..3c050804f01d 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -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; @@ -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. */ @@ -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. @@ -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 @@ -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: @@ -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() { @@ -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); } } @@ -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); 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 8137c36ea350..43a396d8e5d7 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -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; @@ -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; @@ -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 @@ -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); @@ -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); @@ -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<>(); @@ -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); @@ -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); @@ -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); @@ -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); @@ -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); + } } |