Default resolution settings [Backend]
Added new APIs to DisplayManager to:
1. set the user preferred display mode,
2. clear user preferred display mode and
3. wheteher user has chosen a display mode
These new settings are stored in Settings.Global.
Bug: 192332479
Test: atest DefaultDisplayModeTest
Test: atest LocalDisplayAdapterTest
Change-Id: I3a60e271c7ec23c84e74487e89b3e29ca91e8a40
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b8ec6fc..bbdf8e8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1179,12 +1179,15 @@
public final class DisplayManager {
method public boolean areUserDisabledHdrTypesAllowed();
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearUserPreferredDisplayMode();
method @NonNull public int[] getUserDisabledHdrTypes();
+ method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode();
method public boolean isMinimalPostProcessingRequested(int);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]);
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2
field public static final int SWITCHING_TYPE_NONE = 0; // 0x0
@@ -2167,6 +2170,9 @@
field public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
field public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
field public static final String USER_DISABLED_HDR_FORMATS = "user_disabled_hdr_formats";
+ field public static final String USER_PREFERRED_REFRESH_RATE = "user_preferred_refresh_rate";
+ field public static final String USER_PREFERRED_RESOLUTION_HEIGHT = "user_preferred_resolution_height";
+ field public static final String USER_PREFERRED_RESOLUTION_WIDTH = "user_preferred_resolution_width";
field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
}
@@ -2734,6 +2740,7 @@
}
public final class Display {
+ method @NonNull public android.view.Display.Mode getDefaultMode();
method @NonNull public int[] getReportedHdrTypes();
method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
method public int getType();
@@ -2747,6 +2754,11 @@
field public static final int TYPE_WIFI = 3; // 0x3
}
+ public static final class Display.Mode implements android.os.Parcelable {
+ ctor public Display.Mode(int, int, float);
+ method public boolean matches(int, int, float);
+ }
+
public class FocusFinder {
method public static void sort(android.view.View[], int, int, android.view.ViewGroup, boolean);
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 73961ff..209ee40e 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1113,6 +1113,44 @@
}
/**
+ * Sets the default display mode, according to the refresh rate and the resolution chosen by the
+ * user.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public void setUserPreferredDisplayMode(@NonNull Display.Mode mode) {
+ // Create a new object containing default values for the unused fields like mode ID and
+ // alternative refresh rates.
+ Display.Mode preferredMode = new Display.Mode(mode.getPhysicalWidth(),
+ mode.getPhysicalHeight(), mode.getRefreshRate());
+ mGlobal.setUserPreferredDisplayMode(preferredMode);
+ }
+
+ /**
+ * Removes the user preferred display mode.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public void clearUserPreferredDisplayMode() {
+ mGlobal.setUserPreferredDisplayMode(null);
+ }
+
+ /**
+ * Returns the user preferred display mode.
+ *
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ public Display.Mode getUserPreferredDisplayMode() {
+ return mGlobal.getUserPreferredDisplayMode();
+ }
+
+ /**
* When enabled the app requested mode is always selected regardless of user settings and
* policies for low brightness, low battery, etc.
*
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 1fec3c9f..75155bb 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -877,6 +877,29 @@
}
/**
+ * Sets the default display mode, according to the refresh rate and the resolution chosen by the
+ * user.
+ */
+ public void setUserPreferredDisplayMode(Display.Mode mode) {
+ try {
+ mDm.setUserPreferredDisplayMode(mode);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the user preferred display mode.
+ */
+ public Display.Mode getUserPreferredDisplayMode() {
+ try {
+ return mDm.getUserPreferredDisplayMode();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* When enabled the app requested display resolution and refresh rate is always selected
* in DisplayModeDirector regardless of user settings and policies for low brightness, low
* battery etc.
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 1162146..b3be9da 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -27,6 +27,7 @@
import android.hardware.display.WifiDisplay;
import android.hardware.display.WifiDisplayStatus;
import android.media.projection.IMediaProjection;
+import android.view.Display.Mode;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -162,6 +163,11 @@
// based on hardware capability.
int getPreferredWideGamutColorSpaceId();
+ // Sets the user preferred display mode.
+ // Requires WRITE_SECURE_SETTINGS permission.
+ void setUserPreferredDisplayMode(in Mode mode);
+ Mode getUserPreferredDisplayMode();
+
// When enabled the app requested display resolution and refresh rate is always selected
// in DisplayModeDirector regardless of user settings and policies for low brightness, low
// battery etc.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a5b76d3..3c3b237 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14602,6 +14602,38 @@
public static final int HEADS_UP_ON = 1;
/**
+ * The refresh rate chosen by the user.
+ *
+ * @hide
+ */
+ @TestApi
+ @Readable
+ @SuppressLint("NoSettingsProvider")
+ public static final String USER_PREFERRED_REFRESH_RATE = "user_preferred_refresh_rate";
+
+ /**
+ * The resolution height chosen by the user.
+ *
+ * @hide
+ */
+ @TestApi
+ @Readable
+ @SuppressLint("NoSettingsProvider")
+ public static final String USER_PREFERRED_RESOLUTION_HEIGHT =
+ "user_preferred_resolution_height";
+
+ /**
+ * The resolution width chosen by the user.
+ *
+ * @hide
+ */
+ @TestApi
+ @Readable
+ @SuppressLint("NoSettingsProvider")
+ public static final String USER_PREFERRED_RESOLUTION_WIDTH =
+ "user_preferred_resolution_width";
+
+ /**
* The name of the device
*/
@Readable
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index e7ff978..8259a9d 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1000,6 +1000,18 @@
}
/**
+ * Returns the default mode of the display.
+ * @hide
+ */
+ @TestApi
+ public @NonNull Mode getDefaultMode() {
+ synchronized (mLock) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.getDefaultMode();
+ }
+ }
+
+ /**
* Gets the supported modes of this display.
*/
public Mode[] getSupportedModes() {
@@ -1699,6 +1711,11 @@
*/
public static final Mode[] EMPTY_ARRAY = new Mode[0];
+ /**
+ * @hide
+ */
+ public static final int INVALID_MODE_ID = -1;
+
private final int mModeId;
private final int mWidth;
private final int mHeight;
@@ -1709,6 +1726,14 @@
/**
* @hide
*/
+ @TestApi
+ public Mode(int width, int height, float refreshRate) {
+ this(INVALID_MODE_ID, width, height, refreshRate, new float[0]);
+ }
+
+ /**
+ * @hide
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Mode(int modeId, int width, int height, float refreshRate) {
this(modeId, width, height, refreshRate, new float[0]);
@@ -1804,6 +1829,7 @@
*
* @hide
*/
+ @TestApi
public boolean matches(int width, int height, float refreshRate) {
return mWidth == width &&
mHeight == height &&
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 9972eba..9e5d122 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1820,11 +1820,6 @@
* @hide
*/
public static final class DisplayMode {
- /**
- * Invalid display config id.
- */
- public static final int INVALID_DISPLAY_MODE_ID = -1;
-
public int id;
public int width;
public int height;
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index 80b7e10..6f42d59 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -81,6 +81,9 @@
Settings.Global.POWER_BUTTON_LONG_PRESS,
Settings.Global.AUTOMATIC_POWER_SAVE_MODE,
Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT,
- Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS
+ Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS,
+ Settings.Global.USER_PREFERRED_REFRESH_RATE,
+ Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT,
+ Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 0a75eb8..93f900d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -21,6 +21,7 @@
import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_FLOAT_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.PERCENTAGE_INTEGER_VALIDATOR;
@@ -321,6 +322,9 @@
VALIDATORS.put(Global.Wearable.COMBINED_LOCATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.WRIST_ORIENTATION_MODE,
new DiscreteValueValidator(new String[] {"0", "1", "2", "3"}));
+ VALIDATORS.put(Global.USER_PREFERRED_REFRESH_RATE, NON_NEGATIVE_FLOAT_VALIDATOR);
+ VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_HEIGHT, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_WIDTH, ANY_INTEGER_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index 223cc51a..49012b0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -74,6 +74,20 @@
}
};
+ public static final Validator NON_NEGATIVE_FLOAT_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ if (value == null) {
+ return true;
+ }
+ try {
+ return Float.parseFloat(value) >= 0.0f;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ };
+
public static final Validator URI_VALIDATOR = new Validator() {
@Override
public boolean validate(@Nullable String value) {
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 806bcc2..def9685 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -206,6 +206,13 @@
DisplayModeDirector.DesiredDisplayModeSpecs displayModeSpecs) {}
/**
+ * Sets the user preferred display mode. Removes the user preferred display mode and sets
+ * default display mode as the mode chosen by HAL, if 'mode' is null
+ * Returns true if the mode set by user is supported by the display.
+ */
+ public void setUserPreferredDisplayModeLocked(Display.Mode mode) { }
+
+ /**
* Sets the requested color mode.
*/
public void setRequestedColorModeLocked(int colorMode) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 73bcea6..c964e37 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -130,6 +130,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
@@ -213,6 +214,9 @@
private int[] mUserDisabledHdrTypes = {};
private boolean mAreUserDisabledHdrTypesAllowed = true;
+ // Display mode chosen by user.
+ private Display.Mode mUserPreferredMode;
+
// The synchronization root for the display manager.
// This lock guards most of the display manager's state.
// NOTE: This is synchronized on while holding WindowManagerService.mWindowMap so never call
@@ -581,6 +585,7 @@
updateSettingsLocked();
updateUserDisabledHdrTypesFromSettingsLocked();
+ updateUserPreferredDisplayModeSettingsLocked();
}
mDisplayModeDirector.setDesiredDisplayModeSpecsListener(
@@ -795,9 +800,8 @@
mUserDisabledHdrTypes[i] = Integer.parseInt(userDisabledHdrTypeStrings[i]);
}
} catch (NumberFormatException e) {
- Slog.e(TAG,
- "Failed to parse USER_DISABLED_HDR_FORMATS. "
- + "Clearing the setting.", e);
+ Slog.e(TAG, "Failed to parse USER_DISABLED_HDR_FORMATS. "
+ + "Clearing the setting.", e);
clearUserDisabledHdrTypesLocked();
}
} else {
@@ -813,6 +817,17 @@
}
}
+ private void updateUserPreferredDisplayModeSettingsLocked() {
+ final float refreshRate = Settings.Global.getFloat(mContext.getContentResolver(),
+ Settings.Global.USER_PREFERRED_REFRESH_RATE, 0.0f);
+ final int height = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, -1);
+ final int width = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, -1);
+ Display.Mode mode = new Display.Mode(height, width, refreshRate);
+ mUserPreferredMode = isResolutionAndRefreshRateValid(mode) ? mode : null;
+ }
+
private DisplayInfo getDisplayInfoForFrameRateOverride(DisplayEventReceiver.FrameRateOverride[]
frameRateOverrides, DisplayInfo info, int callingUid) {
float frameRateHz = 0;
@@ -1259,6 +1274,9 @@
recordStableDisplayStatsIfNeededLocked(display);
recordTopInsetLocked(display);
}
+ if (mUserPreferredMode != null) {
+ device.setUserPreferredDisplayModeLocked(mUserPreferredMode);
+ }
addDisplayPowerControllerLocked(display);
mDisplayStates.append(displayId, Display.STATE_UNKNOWN);
@@ -1429,6 +1447,39 @@
return mWideColorSpace.getId();
}
+ void setUserPreferredDisplayModeInternal(Display.Mode mode) {
+ synchronized (mSyncRoot) {
+ if (Objects.equals(mUserPreferredMode, mode)) {
+ return;
+ }
+
+ if (mode != null && !isResolutionAndRefreshRateValid(mode)) {
+ throw new IllegalArgumentException("width, height and refresh rate of mode should "
+ + "be greater than 0");
+ }
+ mUserPreferredMode = mode;
+
+ final int resolutionHeight = mode == null ? -1 : mode.getPhysicalHeight();
+ final int resolutionWidth = mode == null ? -1 : mode.getPhysicalWidth();
+ final float refreshRate = mode == null ? 0.0f : mode.getRefreshRate();
+ Settings.Global.putFloat(mContext.getContentResolver(),
+ Settings.Global.USER_PREFERRED_REFRESH_RATE, refreshRate);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, resolutionHeight);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, resolutionWidth);
+ mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
+ device.setUserPreferredDisplayModeLocked(mode);
+ });
+ }
+ }
+
+ private Display.Mode getUserPreferredDisplayModeInternal() {
+ synchronized (mSyncRoot) {
+ return mUserPreferredMode;
+ }
+ }
+
void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) {
mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled);
}
@@ -2019,6 +2070,10 @@
pw.println(" mStableDisplaySize=" + mStableDisplaySize);
pw.println(" mMinimumBrightnessCurve=" + mMinimumBrightnessCurve);
+ if (mUserPreferredMode != null) {
+ pw.println(mUserPreferredMode.toString());
+ }
+
pw.println();
if (!mAreUserDisabledHdrTypesAllowed) {
pw.println(" mUserDisabledHdrTypes: size=" + mUserDisabledHdrTypes.length);
@@ -2097,6 +2152,11 @@
return floatArray;
}
+ private static boolean isResolutionAndRefreshRateValid(Display.Mode mode) {
+ return mode.getPhysicalWidth() > 0 && mode.getPhysicalHeight() > 0
+ && mode.getRefreshRate() > 0.0f;
+ }
+
/**
* This is the object that everything in the display manager locks on.
* We make it an inner class within the {@link DisplayManagerService} to so that it is
@@ -3084,6 +3144,29 @@
}
@Override // Binder call
+ public void setUserPreferredDisplayMode(Display.Mode mode) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.WRITE_SECURE_SETTINGS,
+ "Permission required to set the user preferred display mode.");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setUserPreferredDisplayModeInternal(mode);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public Display.Mode getUserPreferredDisplayMode() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getUserPreferredDisplayModeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index be4ec71..fce3fd53 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -97,8 +97,6 @@
// specific display.
private static final int GLOBAL_ID = -1;
- private static final int INVALID_DISPLAY_MODE_ID = -1;
-
private static final float FLOAT_TOLERANCE = RefreshRateRange.FLOAT_TOLERANCE;
private final Object mLock = new Object();
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 67df565..dbe17b7 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static android.view.Display.Mode.INVALID_MODE_ID;
+
import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Resources;
@@ -65,8 +67,6 @@
private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
- private static final int NO_DISPLAY_MODE_ID = 0;
-
private final LongSparseArray<LocalDisplayDevice> mDevices = new LongSparseArray<>();
private final Injector mInjector;
@@ -191,9 +191,11 @@
// This is only set in the runnable returned from requestDisplayStateLocked.
private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
private float mSdrBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- private int mDefaultModeId;
+ private int mDefaultModeId = INVALID_MODE_ID;
private int mDefaultModeGroup;
- private int mActiveModeId;
+ private int mUserPreferredModeId = INVALID_MODE_ID;
+ private Display.Mode mUserPreferredMode;
+ private int mActiveModeId = INVALID_MODE_ID;
private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
new DisplayModeDirector.DesiredDisplayModeSpecs();
private boolean mDisplayModeSpecsInvalid;
@@ -323,7 +325,7 @@
// Check whether SurfaceFlinger or the display device changed the active mode out from
// under us.
- if (mActiveModeId != NO_DISPLAY_MODE_ID
+ if (mActiveModeId != INVALID_MODE_ID
&& mActiveModeId != activeRecord.mMode.getModeId()) {
Slog.d(TAG, "The active mode was changed from SurfaceFlinger or the display"
+ " device to " + activeRecord.mMode);
@@ -334,12 +336,12 @@
// Check whether surface flinger spontaneously changed display config specs out from
// under us. If so, schedule a traversal to reapply our display config specs.
- if (mDisplayModeSpecs.baseModeId != NO_DISPLAY_MODE_ID) {
+ if (mDisplayModeSpecs.baseModeId != INVALID_MODE_ID) {
int activeBaseMode = findMatchingModeIdLocked(modeSpecs.defaultMode);
// If we can't map the defaultMode index to a mode, then the physical display
// modes must have changed, and the code below for handling changes to the
// list of available modes will take care of updating display mode specs.
- if (activeBaseMode == NO_DISPLAY_MODE_ID
+ if (activeBaseMode == INVALID_MODE_ID
|| mDisplayModeSpecs.baseModeId != activeBaseMode
|| mDisplayModeSpecs.primaryRefreshRateRange.min
!= modeSpecs.primaryRefreshRateMin
@@ -366,7 +368,7 @@
}
// For a new display, we need to initialize the default mode ID.
- if (mDefaultModeId == NO_DISPLAY_MODE_ID) {
+ if (mDefaultModeId == INVALID_MODE_ID) {
mDefaultModeId = activeRecord.mMode.getModeId();
mDefaultModeGroup = mActiveSfDisplayMode.group;
} else if (modesAdded && activeModeChanged) {
@@ -383,7 +385,7 @@
// Determine whether the display mode specs' base mode is still there.
if (mSupportedModes.indexOfKey(mDisplayModeSpecs.baseModeId) < 0) {
- if (mDisplayModeSpecs.baseModeId != NO_DISPLAY_MODE_ID) {
+ if (mDisplayModeSpecs.baseModeId != INVALID_MODE_ID) {
Slog.w(TAG,
"DisplayModeSpecs base mode no longer available, using currently"
+ " active mode.");
@@ -392,13 +394,17 @@
mDisplayModeSpecsInvalid = true;
}
+ if (mUserPreferredMode != null) {
+ mUserPreferredModeId = findUserPreferredModeIdLocked(mUserPreferredMode);
+ }
+
// Determine whether the active mode is still there.
if (mSupportedModes.indexOfKey(mActiveModeId) < 0) {
- if (mActiveModeId != NO_DISPLAY_MODE_ID) {
+ if (mActiveModeId != INVALID_MODE_ID) {
Slog.w(TAG, "Active display mode no longer available, reverting to default"
+ " mode.");
}
- mActiveModeId = mDefaultModeId;
+ mActiveModeId = getPreferredModeId();
}
// Schedule traversals so that we apply pending changes.
@@ -414,6 +420,12 @@
return mDisplayDeviceConfig;
}
+ private int getPreferredModeId() {
+ return mUserPreferredModeId != INVALID_MODE_ID
+ ? mUserPreferredModeId
+ : mDefaultModeId;
+ }
+
private void loadDisplayDeviceConfig() {
// Load display device config
final Context context = getOverlayContext();
@@ -561,7 +573,7 @@
mInfo.width = mActiveSfDisplayMode.width;
mInfo.height = mActiveSfDisplayMode.height;
mInfo.modeId = mActiveModeId;
- mInfo.defaultModeId = mDefaultModeId;
+ mInfo.defaultModeId = getPreferredModeId();
mInfo.supportedModes = getDisplayModes(mSupportedModes);
mInfo.colorMode = mActiveColorMode;
mInfo.allmSupported = mAllmSupported;
@@ -823,6 +835,17 @@
}
@Override
+ public void setUserPreferredDisplayModeLocked(Display.Mode mode) {
+ final int oldModeId = getPreferredModeId();
+ mUserPreferredMode = mode;
+ mUserPreferredModeId = findUserPreferredModeIdLocked(mode);
+
+ if (oldModeId != getPreferredModeId()) {
+ updateDeviceInfoLocked();
+ }
+ }
+
+ @Override
public void setRequestedColorModeLocked(int colorMode) {
requestColorModeLocked(colorMode);
}
@@ -903,7 +926,7 @@
}
mActiveSfDisplayMode = getModeById(mSfDisplayModes, activeSfModeId);
mActiveModeId = findMatchingModeIdLocked(activeSfModeId);
- if (mActiveModeId == NO_DISPLAY_MODE_ID) {
+ if (mActiveModeId == INVALID_MODE_ID) {
Slog.w(TAG, "In unknown mode after setting allowed modes"
+ ", activeModeId=" + activeSfModeId);
}
@@ -988,6 +1011,7 @@
pw.println("mActiveModeId=" + mActiveModeId);
pw.println("mActiveColorMode=" + mActiveColorMode);
pw.println("mDefaultModeId=" + mDefaultModeId);
+ pw.println("mUserPreferredModeId=" + mUserPreferredModeId);
pw.println("mState=" + Display.stateToString(mState));
pw.println("mBrightnessState=" + mBrightnessState);
pw.println("mBacklightAdapter=" + mBacklightAdapter);
@@ -1010,13 +1034,12 @@
}
private int findDisplayModeIdLocked(int modeId, int modeGroup) {
- int matchingModeId = SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID;
+ int matchingModeId = INVALID_MODE_ID;
DisplayModeRecord record = mSupportedModes.get(modeId);
if (record != null) {
for (SurfaceControl.DisplayMode mode : mSfDisplayModes) {
if (record.hasMatchingMode(mode)) {
- if (matchingModeId
- == SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID) {
+ if (matchingModeId == INVALID_MODE_ID) {
matchingModeId = mode.id;
}
@@ -1030,11 +1053,25 @@
return matchingModeId;
}
+ private int findUserPreferredModeIdLocked(Display.Mode userPreferredMode) {
+ if (userPreferredMode != null) {
+ for (int i = 0; i < mSupportedModes.size(); i++) {
+ Display.Mode supportedMode = mSupportedModes.valueAt(i).mMode;
+ if (userPreferredMode.matches(supportedMode.getPhysicalWidth(),
+ supportedMode.getPhysicalHeight(),
+ supportedMode.getRefreshRate())) {
+ return supportedMode.getModeId();
+ }
+ }
+ }
+ return INVALID_MODE_ID;
+ }
+
private int findMatchingModeIdLocked(int sfModeId) {
SurfaceControl.DisplayMode mode = getModeById(mSfDisplayModes, sfModeId);
if (mode == null) {
Slog.e(TAG, "Invalid display mode ID " + sfModeId);
- return NO_DISPLAY_MODE_ID;
+ return INVALID_MODE_ID;
}
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
@@ -1042,7 +1079,7 @@
return record.mMode.getModeId();
}
}
- return NO_DISPLAY_MODE_ID;
+ return INVALID_MODE_ID;
}
private void updateDeviceInfoLocked() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 28cdd63..b5ad459 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -336,6 +336,62 @@
}
@Test
+ public void testAfterDisplayChange_DefaultDisplayModeIsUpdated() throws Exception {
+ SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(0, 1920, 1080, 60f);
+ SurfaceControl.DisplayMode[] modes =
+ new SurfaceControl.DisplayMode[]{displayMode};
+ FakeDisplay display = new FakeDisplay(PORT_A, modes, 0);
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays).isEmpty();
+
+ DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(
+ 0).getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode);
+
+ Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
+ assertThat(matches(defaultMode, displayMode)).isTrue();
+
+ // Set the display mode to an unsupported mode
+ SurfaceControl.DisplayMode displayMode2 = createFakeDisplayMode(1, 1920, 1080, 120f);
+ mListener.addedDisplays.get(0).setUserPreferredDisplayModeLocked(
+ new Display.Mode(displayMode2.width, displayMode2.height,
+ displayMode2.refreshRate));
+ updateAvailableDisplays();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
+ assertThat(matches(defaultMode, displayMode2)).isFalse();
+
+ // Change the display
+ modes = new SurfaceControl.DisplayMode[]{displayMode, displayMode2};
+ display.dynamicInfo.supportedDisplayModes = modes;
+ setUpDisplay(display);
+ mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertTrue(mListener.traversalRequested);
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+
+ DisplayDevice displayDevice = mListener.changedDisplays.get(0);
+ displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
+ displayDeviceInfo = mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode2);
+
+ defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
+ assertThat(matches(defaultMode, displayMode2)).isTrue();
+ }
+
+ @Test
public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception {
SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(0, 1920, 1080, 60f);
SurfaceControl.DisplayMode[] modes =
@@ -356,12 +412,10 @@
assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode);
Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
- assertThat(defaultMode.matches(displayMode.width, displayMode.height,
- displayMode.refreshRate)).isTrue();
+ assertThat(matches(defaultMode, displayMode)).isTrue();
Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
- assertThat(activeMode.matches(displayMode.width, displayMode.height,
- displayMode.refreshRate)).isTrue();
+ assertThat(matches(activeMode, displayMode)).isTrue();
// Change the display
SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(1, 3840, 2160, 60f);
@@ -385,12 +439,10 @@
assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo);
activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
- assertThat(activeMode.matches(addedDisplayInfo.width, addedDisplayInfo.height,
- addedDisplayInfo.refreshRate)).isTrue();
+ assertThat(matches(activeMode, addedDisplayInfo)).isTrue();
defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
- assertThat(defaultMode.matches(addedDisplayInfo.width, addedDisplayInfo.height,
- addedDisplayInfo.refreshRate)).isTrue();
+ assertThat(matches(defaultMode, addedDisplayInfo)).isTrue();
}
@Test
@@ -918,4 +970,9 @@
});
return mockArray;
}
+
+ private boolean matches(Display.Mode a, SurfaceControl.DisplayMode b) {
+ return a.getPhysicalWidth() == b.width && a.getPhysicalHeight() == b.height
+ && Float.floatToIntBits(a.getRefreshRate()) == Float.floatToIntBits(b.refreshRate);
+ }
}