summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/config.xml18
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java82
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java95
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);
+ }
}