summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java13
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig11
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java81
-rw-r--r--services/core/java/com/android/server/display/mode/Vote.java2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java61
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java103
6 files changed, 243 insertions, 28 deletions
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 5284d1c423f6..43860b289689 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -207,6 +207,10 @@ public class DisplayManagerFlags {
Flags.FLAG_BLOCK_AUTOBRIGHTNESS_CHANGES_ON_STYLUS_USAGE,
Flags::blockAutobrightnessChangesOnStylusUsage
);
+ private final FlagState mIsUserRefreshRateForExternalDisplayEnabled = new FlagState(
+ Flags.FLAG_ENABLE_USER_REFRESH_RATE_FOR_EXTERNAL_DISPLAY,
+ Flags::enableUserRefreshRateForExternalDisplay
+ );
private final FlagState mEnableBatteryStatsForAllDisplays = new FlagState(
Flags.FLAG_ENABLE_BATTERY_STATS_FOR_ALL_DISPLAYS,
@@ -447,6 +451,14 @@ public class DisplayManagerFlags {
}
/**
+ * @return {@code true} if need to use user refresh rate settings for
+ * external displays.
+ */
+ public boolean isUserRefreshRateForExternalDisplayEnabled() {
+ return mIsUserRefreshRateForExternalDisplayEnabled.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -491,6 +503,7 @@ public class DisplayManagerFlags {
pw.println(" " + mIdleScreenConfigInSubscribingLightSensor);
pw.println(" " + mEnableBatteryStatsForAllDisplays);
pw.println(" " + mBlockAutobrightnessChangesOnStylusUsage);
+ pw.println(" " + mIsUserRefreshRateForExternalDisplayEnabled);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 252ed09fd125..3d7c8c74be78 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -373,3 +373,14 @@ flag {
bug: "352411468"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_user_refresh_rate_for_external_display"
+ namespace: "display_manager"
+ description: "Apply refresh rate from user preferred display mode to external displays"
+ bug: "370657357"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
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 18e0d6ee5ea3..ffa64bfcf29f 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -1194,6 +1194,13 @@ public class DisplayModeDirector {
@GuardedBy("mLock")
private void updateRefreshRateSettingLocked(float minRefreshRate, float peakRefreshRate,
float defaultRefreshRate, int displayId) {
+ if (mDisplayObserver.isExternalDisplayLocked(displayId)) {
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "skip updateRefreshRateSettingLocked for external display "
+ + displayId);
+ }
+ return;
+ }
// 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
@@ -1243,6 +1250,8 @@ public class DisplayModeDirector {
}
private void removeRefreshRateSetting(int displayId) {
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE,
+ null);
mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
null);
mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
@@ -1458,11 +1467,11 @@ public class DisplayModeDirector {
public void onDisplayAdded(int displayId) {
updateDisplayDeviceConfig(displayId);
DisplayInfo displayInfo = getDisplayInfo(displayId);
+ registerExternalDisplay(displayInfo);
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
updateUserSettingDisplayPreferredSize(displayInfo);
updateDisplaysPeakRefreshRateAndResolution(displayInfo);
- addDisplaysSynchronizedPeakRefreshRate(displayInfo);
}
@Override
@@ -1477,7 +1486,7 @@ public class DisplayModeDirector {
updateLayoutLimitedFrameRate(displayId, null);
removeUserSettingDisplayPreferredSize(displayId);
removeDisplaysPeakRefreshRateAndResolution(displayId);
- removeDisplaysSynchronizedPeakRefreshRate(displayId);
+ unregisterExternalDisplay(displayId);
}
@Override
@@ -1489,6 +1498,30 @@ public class DisplayModeDirector {
updateUserSettingDisplayPreferredSize(displayInfo);
}
+ private void registerExternalDisplay(DisplayInfo displayInfo) {
+ if (displayInfo == null || displayInfo.type != Display.TYPE_EXTERNAL) {
+ return;
+ }
+ synchronized (mLock) {
+ mExternalDisplaysConnected.add(displayInfo.displayId);
+ if (mExternalDisplaysConnected.size() == 1) {
+ addDisplaysSynchronizedPeakRefreshRate();
+ }
+ }
+ }
+
+ private void unregisterExternalDisplay(int displayId) {
+ synchronized (mLock) {
+ if (!isExternalDisplayLocked(displayId)) {
+ return;
+ }
+ mExternalDisplaysConnected.remove(displayId);
+ if (mExternalDisplaysConnected.isEmpty()) {
+ removeDisplaysSynchronizedPeakRefreshRate();
+ }
+ }
+ }
+
boolean isExternalDisplayLocked(int displayId) {
return mExternalDisplaysConnected.contains(displayId);
}
@@ -1534,10 +1567,24 @@ public class DisplayModeDirector {
return;
}
- mVotesStorage.updateVote(info.displayId,
- Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE,
- Vote.forSize(/* width */ preferredMode.getPhysicalWidth(),
- /* height */ preferredMode.getPhysicalHeight()));
+ if (info.type == Display.TYPE_EXTERNAL
+ && mDisplayManagerFlags.isUserRefreshRateForExternalDisplayEnabled()
+ && !isRefreshRateSynchronizationEnabled()) {
+ mVotesStorage.updateVote(info.displayId,
+ Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE,
+ Vote.forSizeAndPhysicalRefreshRatesRange(
+ /* minWidth */ preferredMode.getPhysicalWidth(),
+ /* minHeight */ preferredMode.getPhysicalHeight(),
+ /* width */ preferredMode.getPhysicalWidth(),
+ /* height */ preferredMode.getPhysicalHeight(),
+ /* minRefreshRate */ preferredMode.getRefreshRate(),
+ /* maxRefreshRate */ preferredMode.getRefreshRate()));
+ } else {
+ mVotesStorage.updateVote(info.displayId,
+ Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE,
+ Vote.forSize(/* width */ preferredMode.getPhysicalWidth(),
+ /* height */ preferredMode.getPhysicalHeight()));
+ }
}
@Nullable
@@ -1584,17 +1631,10 @@ public class DisplayModeDirector {
* Sets 60Hz target refresh rate as the vote with
* {@link Vote#PRIORITY_SYNCHRONIZED_REFRESH_RATE} priority.
*/
- private void addDisplaysSynchronizedPeakRefreshRate(@Nullable final DisplayInfo info) {
- if (info == null || info.type != Display.TYPE_EXTERNAL
- || !isRefreshRateSynchronizationEnabled()) {
+ private void addDisplaysSynchronizedPeakRefreshRate() {
+ if (!isRefreshRateSynchronizationEnabled()) {
return;
}
- synchronized (mLock) {
- mExternalDisplaysConnected.add(info.displayId);
- if (mExternalDisplaysConnected.size() != 1) {
- return;
- }
- }
// set minRefreshRate as the max refresh rate.
mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE,
Vote.forPhysicalRefreshRates(
@@ -1610,19 +1650,10 @@ public class DisplayModeDirector {
+ SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
}
- private void removeDisplaysSynchronizedPeakRefreshRate(final int displayId) {
+ private void removeDisplaysSynchronizedPeakRefreshRate() {
if (!isRefreshRateSynchronizationEnabled()) {
return;
}
- synchronized (mLock) {
- if (!isExternalDisplayLocked(displayId)) {
- return;
- }
- mExternalDisplaysConnected.remove(displayId);
- if (!mExternalDisplaysConnected.isEmpty()) {
- return;
- }
- }
mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE, null);
mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE, null);
}
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index 459f9a6e8f13..f5abb0561ce7 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -44,7 +44,7 @@ interface Vote {
// It votes [minRefreshRate, Float.POSITIVE_INFINITY]
int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
- // User setting preferred display resolution.
+ // User setting preferred display resolution, for external displays also includes refresh rate.
int PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE = 4;
// APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
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 d91f154c1b87..58f0ab4411bc 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
@@ -1872,6 +1872,60 @@ public class DisplayModeDirectorTest {
}
@Test
+ public void testPeakRefreshRate_notAppliedToExternalDisplays() {
+ when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
+ .thenReturn(true);
+ mInjector.mDisplayInfo.type = Display.TYPE_EXTERNAL;
+ DisplayModeDirector director =
+ new DisplayModeDirector(mContext, mHandler, mInjector,
+ mDisplayManagerFlags, mDisplayDeviceConfigProvider);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+ director.getDisplayObserver().onDisplayAdded(DISPLAY_ID);
+ director.getDisplayObserver().onDisplayAdded(DISPLAY_ID_2);
+
+ Display.Mode[] modes1 = new Display.Mode[] {
+ new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
+ /* refreshRate= */ 60),
+ new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
+ /* refreshRate= */ 130),
+ };
+ Display.Mode[] modes2 = new Display.Mode[] {
+ new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
+ /* refreshRate= */ 60),
+ new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
+ /* refreshRate= */ 140),
+ };
+ SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
+ supportedModesByDisplay.put(DISPLAY_ID, modes1);
+ supportedModesByDisplay.put(DISPLAY_ID_2, modes2);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+ director.injectSupportedModesByDisplay(supportedModesByDisplay);
+
+ // Disable Smooth Display
+ setPeakRefreshRate(RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE);
+
+ Vote vote1 = director.getVote(DISPLAY_ID,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ Vote vote2 = director.getVote(DISPLAY_ID_2,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ assertThat(vote1).isNull();
+ assertThat(vote2).isNull();
+
+ // Enable Smooth Display
+ setPeakRefreshRate(Float.POSITIVE_INFINITY);
+
+ vote1 = director.getVote(DISPLAY_ID,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ vote2 = director.getVote(DISPLAY_ID_2,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ assertThat(vote1).isNull();
+ assertThat(vote2).isNull();
+ }
+
+ @Test
public void testPeakRefreshRate_DisplayChanged() {
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
@@ -1968,8 +2022,9 @@ public class DisplayModeDirectorTest {
@Test
@Parameters({
"true, true, 60",
- "false, true, 50",
- "true, false, 50"
+ "false, true, 60",
+ "true, false, 50",
+ "false, false, 50"
})
public void testExternalDisplayMaxRefreshRate(boolean isRefreshRateSynchronizationEnabled,
boolean isExternalDisplay, float expectedMaxRenderFrameRate) {
@@ -3810,6 +3865,7 @@ public class DisplayModeDirectorTest {
SensorManagerInternal sensorManagerInternal) {
mDeviceConfig = new FakeDeviceConfig();
mDisplayInfo = new DisplayInfo();
+ mDisplayInfo.type = Display.TYPE_INTERNAL;
mDisplayInfo.defaultModeId = MODE_ID;
mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
800, 600, /* refreshRate= */ 60)};
@@ -3856,6 +3912,7 @@ public class DisplayModeDirectorTest {
@Override
public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
displayInfo.copyFrom(mDisplayInfo);
+ displayInfo.displayId = displayId;
return mDisplayInfoValid;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
index 5e240cf66674..e3f150e9fbc1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -189,6 +189,7 @@ public class DisplayObserverTest {
@Test
public void testExternalDisplay_voteUserPreferredMode() {
when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserRefreshRateForExternalDisplayEnabled()).thenReturn(false);
var preferredMode = mExternalDisplayModes[5];
mExternalDisplayUserPreferredModeId = preferredMode.getModeId();
var expectedVote = Vote.forSize(
@@ -229,6 +230,108 @@ public class DisplayObserverTest {
.isEqualTo(null);
}
+ /** Vote for user preferred mode */
+ @Test
+ public void testDefaultDisplay_voteUserPreferredMode() {
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserRefreshRateForExternalDisplayEnabled()).thenReturn(true);
+ var preferredMode = mInternalDisplayModes[5];
+ mInternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ var expectedVote = Vote.forSize(
+ preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight());
+ init();
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ mObserver.onDisplayAdded(DEFAULT_DISPLAY);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedVote);
+
+ mInternalDisplayUserPreferredModeId = INVALID_MODE_ID;
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ mObserver.onDisplayChanged(DEFAULT_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+
+ preferredMode = mInternalDisplayModes[4];
+ mInternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ expectedVote = Vote.forSize(
+ preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight());
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ mObserver.onDisplayChanged(DEFAULT_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedVote);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+
+ // Testing that the vote is removed.
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedVote);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ }
+
+ /** Vote for user preferred mode with refresh rate, synchronization vote must be disabled */
+ @Test
+ public void testExternalDisplay_voteUserPreferredMode_withRefreshRate() {
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(false);
+ when(mDisplayManagerFlags.isUserRefreshRateForExternalDisplayEnabled()).thenReturn(true);
+ var preferredMode = mExternalDisplayModes[5];
+ mExternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ var expectedVote = Vote.forSizeAndPhysicalRefreshRatesRange(
+ preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight(),
+ preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight(),
+ preferredMode.getRefreshRate(),
+ preferredMode.getRefreshRate());
+ init();
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedVote);
+
+ mExternalDisplayUserPreferredModeId = INVALID_MODE_ID;
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+
+ preferredMode = mExternalDisplayModes[4];
+ mExternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ expectedVote = Vote.forSizeAndPhysicalRefreshRatesRange(
+ preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight(),
+ preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight(),
+ preferredMode.getRefreshRate(),
+ preferredMode.getRefreshRate());
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedVote);
+
+ // Testing that the vote is removed.
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ }
+
/** External display: Do not apply limit to user preferred mode */
@Test
public void testExternalDisplay_doNotApplyLimitToUserPreferredMode() {