From ecf14412d31109286114ce25556a3af305a0d7b9 Mon Sep 17 00:00:00 2001 From: Kriti Dang Date: Wed, 24 Nov 2021 14:33:52 +0100 Subject: [PATCH] Runtime resolution changes Changed the names of existing display mode APIs. Added similar APIs in Display class, so that resolution and refresh rate settings can be pesisted for each display. The APIs can now be used to set display mode (or only resolution or refreshRate). Bug: 206911689 Test: atest DefaultDisplayModeTest Change-Id: I19655fc347d36e45b5664e2b6faa3d58b5d7004c --- core/api/test-current.txt | 16 +- core/api/test-lint-baseline.txt | 2 + .../hardware/display/DisplayManager.java | 27 ++- .../display/DisplayManagerGlobal.java | 8 +- .../hardware/display/IDisplayManager.aidl | 4 +- core/java/android/view/Display.java | 202 ++++++++++++++++++ .../android/server/display/DisplayDevice.java | 9 + .../server/display/DisplayManagerService.java | 118 +++++++--- .../display/DisplayManagerShellCommand.java | 65 ++++-- .../server/display/LocalDisplayAdapter.java | 21 ++ .../server/display/PersistentDataStore.java | 76 +++++++ .../stats/pull/StatsPullAtomService.java | 3 +- 12 files changed, 487 insertions(+), 64 deletions(-) diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 89dc678360f1..0cfa746044e6 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1140,15 +1140,15 @@ package android.hardware.display { public final class DisplayManager { method public boolean areUserDisabledHdrTypesAllowed(); - method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearUserPreferredDisplayMode(); + method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearGlobalUserPreferredDisplayMode(); + method @Nullable public android.view.Display.Mode getGlobalUserPreferredDisplayMode(); 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_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode); 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.MODIFY_USER_PREFERRED_DISPLAY_MODE) 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 @@ -2689,11 +2689,14 @@ package android.view { } public final class Display { + method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearUserPreferredDisplayMode(); method @NonNull public android.view.Display.Mode getDefaultMode(); method @NonNull public int[] getReportedHdrTypes(); method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut(); method public int getType(); + method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode(); method public boolean hasAccess(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode); field public static final int FLAG_TRUSTED = 128; // 0x80 field public static final int TYPE_EXTERNAL = 2; // 0x2 field public static final int TYPE_INTERNAL = 1; // 0x1 @@ -2708,6 +2711,13 @@ package android.view { method public boolean matches(int, int, float); } + public static final class Display.Mode.Builder { + ctor public Display.Mode.Builder(); + method @NonNull public android.view.Display.Mode build(); + method @NonNull public android.view.Display.Mode.Builder setRefreshRate(float); + method @NonNull public android.view.Display.Mode.Builder setResolution(int, int); + } + public class FocusFinder { method public static void sort(android.view.View[], int, int, android.view.ViewGroup, boolean); } diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index e3690e55f4da..01604e6becf0 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -737,6 +737,8 @@ MissingGetterMatchingBuilder: android.telephony.ims.stub.ImsFeatureConfiguration MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String): +MissingGetterMatchingBuilder: android.view.Display.Mode.Builder#setResolution(int, int): + android.view.Display.Mode does not declare a `getResolution()` method matching method android.view.Display.Mode.Builder.setResolution(int,int) MissingNullability: android.app.Activity#onMovedToDisplay(int, android.content.res.Configuration) parameter #1: diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 00374644d72c..955436fa86e1 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -1132,41 +1132,48 @@ public final class DisplayManager { } /** - * Sets the default display mode, according to the refresh rate and the resolution chosen by the - * user. + * Sets the global default {@link Display.Mode}. The display mode includes preference for + * resolution and refresh rate. The mode change is applied globally, i.e. to all the connected + * displays. If the mode specified is not supported by a connected display, then no mode change + * occurs for that display. * + * @param mode The {@link Display.Mode} to set, which can include resolution and/or + * refresh-rate. It is created using {@link Display.Mode.Builder}. + *` * @hide */ @TestApi @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) - public void setUserPreferredDisplayMode(@NonNull Display.Mode mode) { + public void setGlobalUserPreferredDisplayMode(@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); + mGlobal.setUserPreferredDisplayMode(Display.INVALID_DISPLAY, preferredMode); } /** - * Removes the user preferred display mode. + * Removes the global user preferred display mode. + * User preferred display mode is cleared for all the connected displays. * * @hide */ @TestApi @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) - public void clearUserPreferredDisplayMode() { - mGlobal.setUserPreferredDisplayMode(null); + public void clearGlobalUserPreferredDisplayMode() { + mGlobal.setUserPreferredDisplayMode(Display.INVALID_DISPLAY, null); } /** - * Returns the user preferred display mode. + * Returns the global user preferred display mode. + * If no user preferred mode has been set, or it has been cleared, this method returns null. * * @hide */ @TestApi @Nullable - public Display.Mode getUserPreferredDisplayMode() { - return mGlobal.getUserPreferredDisplayMode(); + public Display.Mode getGlobalUserPreferredDisplayMode() { + return mGlobal.getUserPreferredDisplayMode(Display.INVALID_DISPLAY); } /** diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 01833fda5b95..c35d86362acf 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -881,9 +881,9 @@ public final class DisplayManagerGlobal { * Sets the default display mode, according to the refresh rate and the resolution chosen by the * user. */ - public void setUserPreferredDisplayMode(Display.Mode mode) { + public void setUserPreferredDisplayMode(int displayId, Display.Mode mode) { try { - mDm.setUserPreferredDisplayMode(mode); + mDm.setUserPreferredDisplayMode(displayId, mode); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -892,9 +892,9 @@ public final class DisplayManagerGlobal { /** * Returns the user preferred display mode. */ - public Display.Mode getUserPreferredDisplayMode() { + public Display.Mode getUserPreferredDisplayMode(int displayId) { try { - return mDm.getUserPreferredDisplayMode(); + return mDm.getUserPreferredDisplayMode(displayId); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 82b31d48d5fe..9af277637822 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -166,8 +166,8 @@ interface IDisplayManager { // Sets the user preferred display mode. // Requires MODIFY_USER_PREFERRED_DISPLAY_MODE permission. - void setUserPreferredDisplayMode(in Mode mode); - Mode getUserPreferredDisplayMode(); + void setUserPreferredDisplayMode(int displayId, in Mode mode); + Mode getUserPreferredDisplayMode(int displayId); // 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 diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 3cc51c7e8b51..70266c1717a1 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -19,6 +19,7 @@ package android.view; import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE; import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -137,6 +138,24 @@ public final class Display { */ public static final int INVALID_DISPLAY = -1; + /** + * Invalid resolution width. + * @hide + */ + public static final int INVALID_DISPLAY_WIDTH = -1; + + /** + * Invalid resolution height. + * @hide + */ + public static final int INVALID_DISPLAY_HEIGHT = -1; + + /** + * Invalid refresh rate. + * @hide + */ + public static final float INVALID_DISPLAY_REFRESH_RATE = 0.0f; + /** * The default display group id, which is the display group id of the primary display assuming * there is one. @@ -1169,6 +1188,49 @@ public final class Display { } } + /** + * Sets the default {@link Display.Mode} to use for the display. The display mode includes + * preference for resolution and refresh rate. + * If the mode specified is not supported by the display, then no mode change occurs. + * + * @param mode The {@link Display.Mode} to set, which can include resolution and/or + * refresh-rate. It is created using {@link Display.Mode.Builder}. + *` + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) + 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(mDisplayId, preferredMode); + } + + /** + * Removes the display's user preferred display mode. + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) + public void clearUserPreferredDisplayMode() { + mGlobal.setUserPreferredDisplayMode(mDisplayId, null); + } + + /** + * Returns the display's user preferred display mode. + * + * @hide + */ + @TestApi + @Nullable + public Display.Mode getUserPreferredDisplayMode() { + return mGlobal.getUserPreferredDisplayMode(mDisplayId); + } + + /** * Returns whether this display can be used to display wide color gamut content. * This does not necessarily mean the device itself can render wide color gamut @@ -1709,6 +1771,30 @@ public final class Display { return state == STATE_ON || state == STATE_VR || state == STATE_ON_SUSPEND; } + /** + * Returns true if the specified width is valid. + * @hide + */ + public static boolean isWidthValid(int width) { + return width > 0; + } + + /** + * Returns true if the specified height is valid. + * @hide + */ + public static boolean isHeightValid(int height) { + return height > 0; + } + + /** + * Returns true if the specified refresh-rate is valid. + * @hide + */ + public static boolean isRefreshRateValid(float refreshRate) { + return refreshRate > 0.0f; + } + /** * A mode supported by a given display. * @@ -1845,6 +1931,30 @@ public final class Display { Float.floatToIntBits(mRefreshRate) == Float.floatToIntBits(refreshRate); } + /** + * Returns {@code true} if this mode matches the given parameters, if those parameters are + * valid.

+ * If resolution (width and height) is valid and refresh-rate is not, the method matches + * only resolution. + * If refresh-rate is valid and resolution (width and height) is not, the method matches + * only refresh-rate.

+ * + * @hide + */ + public boolean matchesIfValid(int width, int height, float refreshRate) { + if (!isWidthValid(width) && !isHeightValid(height) + && !isRefreshRateValid(refreshRate)) { + return false; + } + if (isWidthValid(width) != isHeightValid(height)) { + return false; + } + return (!isWidthValid(width) || mWidth == width) + && (!isHeightValid(height) || mHeight == height) + && (!isRefreshRateValid(refreshRate) + || Float.floatToIntBits(mRefreshRate) == Float.floatToIntBits(refreshRate)); + } + /** * Returns {@code true} if this mode equals to the other mode in all parameters except * the refresh rate. @@ -1855,6 +1965,24 @@ public final class Display { return mWidth == other.mWidth && mHeight == other.mHeight; } + /** + * Returns {@code true} if refresh-rate is set for a display mode + * + * @hide + */ + public boolean isRefreshRateSet() { + return mRefreshRate != INVALID_DISPLAY_REFRESH_RATE; + } + + /** + * Returns {@code true} if refresh-rate is set for a display mode + * + * @hide + */ + public boolean isResolutionSet() { + return mWidth != INVALID_DISPLAY_WIDTH && mHeight != INVALID_DISPLAY_HEIGHT; + } + @Override public boolean equals(@Nullable Object other) { if (this == other) { @@ -1923,6 +2051,80 @@ public final class Display { return new Mode[size]; } }; + + /** + * Builder is used to create {@link Display.Mode} objects + * + * @hide + */ + @TestApi + public static final class Builder { + private int mWidth; + private int mHeight; + private float mRefreshRate; + + public Builder() { + mWidth = Display.INVALID_DISPLAY_WIDTH; + mHeight = Display.INVALID_DISPLAY_HEIGHT; + mRefreshRate = Display.INVALID_DISPLAY_REFRESH_RATE; + } + + /** + * Sets the resolution (width and height) of a {@link Display.Mode} + * + * @return Instance of {@link Builder} + */ + @NonNull + public Builder setResolution(int width, int height) { + if (width > 0 && height > 0) { + mWidth = width; + mHeight = height; + } + return this; + } + + /** + * Sets the refresh rate of a {@link Display.Mode} + * + * @return Instance of {@link Builder} + */ + @NonNull + public Builder setRefreshRate(float refreshRate) { + if (refreshRate > 0.0f) { + mRefreshRate = refreshRate; + } + return this; + } + + /** + * Creates the {@link Display.Mode} object. + * + *

+ * If resolution needs to be set, but refresh-rate doesn't matter, create a mode with + * Builder and call setResolution. + * {@code + * Display.Mode mode = + * new Display.Mode.Builder() + * .setResolution(width, height) + * .build(); + * } + *

+ * If refresh-rate needs to be set, but resolution doesn't matter, create a mode with + * Builder and call setRefreshRate. + * {@code + * Display.Mode mode = + * new Display.Mode.Builder() + * .setRefreshRate(refreshRate) + * .build(); + * } + *

+ */ + @NonNull + public Mode build() { + Display.Mode mode = new Mode(mWidth, mHeight, mRefreshRate); + return mode; + } + } } /** diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index def9685b1c1f..d0ce9ef47c35 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -37,6 +37,8 @@ import java.io.PrintWriter; *

*/ abstract class DisplayDevice { + private static final Display.Mode EMPTY_DISPLAY_MODE = new Display.Mode.Builder().build(); + private final DisplayAdapter mDisplayAdapter; private final IBinder mDisplayToken; private final String mUniqueId; @@ -212,6 +214,13 @@ abstract class DisplayDevice { */ public void setUserPreferredDisplayModeLocked(Display.Mode mode) { } + /** + * Returns the user preferred display mode. + */ + public Display.Mode getUserPreferredDisplayModeLocked() { + return EMPTY_DISPLAY_MODE; + } + /** * Sets the requested color mode. */ diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index c0a6abf3a121..931f1eddda92 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -874,11 +874,11 @@ public final class DisplayManagerService extends SystemService { private void updateUserPreferredDisplayModeSettingsLocked() { final float refreshRate = Settings.Global.getFloat(mContext.getContentResolver(), - Settings.Global.USER_PREFERRED_REFRESH_RATE, 0.0f); + Settings.Global.USER_PREFERRED_REFRESH_RATE, Display.INVALID_DISPLAY_REFRESH_RATE); final int height = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, -1); + Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, Display.INVALID_DISPLAY_HEIGHT); final int width = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, -1); + Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, Display.INVALID_DISPLAY_WIDTH); Display.Mode mode = new Display.Mode(width, height, refreshRate); mUserPreferredMode = isResolutionAndRefreshRateValid(mode) ? mode : null; } @@ -1561,8 +1561,11 @@ public final class DisplayManagerService extends SystemService { } if (mUserPreferredMode != null) { device.setUserPreferredDisplayModeLocked(mUserPreferredMode); + } else { + configurePreferredDisplayModeLocked(display); } addDisplayPowerControllerLocked(display); + mDisplayStates.append(displayId, Display.STATE_UNKNOWN); final float brightnessDefault = display.getDisplayInfoLocked().brightnessDefault; @@ -1689,6 +1692,24 @@ public final class DisplayManagerService extends SystemService { } } + private void configurePreferredDisplayModeLocked(LogicalDisplay display) { + final DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); + final Point userPreferredResolution = + mPersistentDataStore.getUserPreferredResolution(device); + final float refreshRate = mPersistentDataStore.getUserPreferredRefreshRate(device); + if (userPreferredResolution == null && Float.isNaN(refreshRate)) { + return; + } + Display.Mode.Builder modeBuilder = new Display.Mode.Builder(); + if (userPreferredResolution != null) { + modeBuilder.setResolution(userPreferredResolution.x, userPreferredResolution.y); + } + if (!Float.isNaN(refreshRate)) { + modeBuilder.setRefreshRate(refreshRate); + } + device.setUserPreferredDisplayModeLocked(modeBuilder.build()); + } + // If we've never recorded stable device stats for this device before and they aren't // explicitly configured, go ahead and record the stable device stats now based on the status // of the default display at first boot. @@ -1732,36 +1753,79 @@ public final class DisplayManagerService extends SystemService { return mWideColorSpace.getId(); } - void setUserPreferredDisplayModeInternal(Display.Mode mode) { + void setUserPreferredDisplayModeInternal(int displayId, Display.Mode mode) { synchronized (mSyncRoot) { - if (Objects.equals(mUserPreferredMode, mode)) { + if (Objects.equals(mUserPreferredMode, mode) && displayId == Display.INVALID_DISPLAY) { return; } - if (mode != null && !isResolutionAndRefreshRateValid(mode)) { + if (mode != null && !isResolutionAndRefreshRateValid(mode) + && displayId == Display.INVALID_DISPLAY) { throw new IllegalArgumentException("width, height and refresh rate of mode should " - + "be greater than 0"); + + "be greater than 0 when setting the global user preferred display mode."); } - 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); - }); + final int resolutionHeight = mode == null ? Display.INVALID_DISPLAY_HEIGHT + : mode.getPhysicalHeight(); + final int resolutionWidth = mode == null ? Display.INVALID_DISPLAY_WIDTH + : mode.getPhysicalWidth(); + final float refreshRate = mode == null ? Display.INVALID_DISPLAY_REFRESH_RATE + : mode.getRefreshRate(); + + storeModeInPersistentDataStoreLocked( + displayId, resolutionWidth, resolutionHeight, refreshRate); + if (displayId != Display.INVALID_DISPLAY) { + setUserPreferredModeForDisplayLocked(displayId, mode); + } else { + mUserPreferredMode = mode; + storeModeInGlobalSettingsLocked( + resolutionWidth, resolutionHeight, refreshRate, mode); + } } } - private Display.Mode getUserPreferredDisplayModeInternal() { + private void storeModeInPersistentDataStoreLocked(int displayId, int resolutionWidth, + int resolutionHeight, float refreshRate) { + DisplayDevice displayDevice = getDeviceForDisplayLocked(displayId); + if (displayDevice == null) { + return; + } + mPersistentDataStore.setUserPreferredResolution( + displayDevice, resolutionWidth, resolutionHeight); + mPersistentDataStore.setUserPreferredRefreshRate(displayDevice, refreshRate); + } + + private void setUserPreferredModeForDisplayLocked(int displayId, Display.Mode mode) { + DisplayDevice displayDevice = getDeviceForDisplayLocked(displayId); + if (displayDevice == null) { + return; + } + displayDevice.setUserPreferredDisplayModeLocked(mode); + } + + private void storeModeInGlobalSettingsLocked( + int resolutionWidth, int resolutionHeight, float refreshRate, Display.Mode mode) { + 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); + }); + } + + Display.Mode getUserPreferredDisplayModeInternal(int displayId) { synchronized (mSyncRoot) { - return mUserPreferredMode; + if (displayId == Display.INVALID_DISPLAY) { + return mUserPreferredMode; + } + DisplayDevice displayDevice = getDeviceForDisplayLocked(displayId); + if (displayDevice == null) { + return null; + } + return displayDevice.getUserPreferredDisplayModeLocked(); } } @@ -2366,7 +2430,7 @@ public final class DisplayManagerService extends SystemService { pw.println(" mMinimumBrightnessCurve=" + mMinimumBrightnessCurve); if (mUserPreferredMode != null) { - pw.println(mUserPreferredMode.toString()); + pw.println(mUserPreferredMode); } pw.println(); @@ -3372,23 +3436,23 @@ public final class DisplayManagerService extends SystemService { } @Override // Binder call - public void setUserPreferredDisplayMode(Display.Mode mode) { + public void setUserPreferredDisplayMode(int displayId, Display.Mode mode) { mContext.enforceCallingOrSelfPermission( Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE, "Permission required to set the user preferred display mode."); final long token = Binder.clearCallingIdentity(); try { - setUserPreferredDisplayModeInternal(mode); + setUserPreferredDisplayModeInternal(displayId, mode); } finally { Binder.restoreCallingIdentity(token); } } @Override // Binder call - public Display.Mode getUserPreferredDisplayMode() { + public Display.Mode getUserPreferredDisplayMode(int displayId) { final long token = Binder.clearCallingIdentity(); try { - return getUserPreferredDisplayModeInternal(); + return getUserPreferredDisplayModeInternal(displayId); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index 9a7ddcb2ff91..a9a1f08c140a 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -113,13 +113,18 @@ class DisplayManagerShellCommand extends ShellCommand { pw.println(" constrain-launcher-metrics [true|false]"); pw.println(" Sets if Display#getRealSize and getRealMetrics should be constrained for "); pw.println(" Launcher."); - pw.println(" set-user-preferred-display-mode WIDTH HEIGHT REFRESH-RATE"); + pw.println(" set-user-preferred-display-mode WIDTH HEIGHT REFRESH-RATE " + + "DISPLAY_ID (optional)"); pw.println(" Sets the user preferred display mode which has fields WIDTH, HEIGHT and " - + "REFRESH-RATE"); - pw.println(" clear-user-preferred-display-mode"); - pw.println(" Clears the user preferred display mode"); - pw.println(" get-user-preferred-display-mode"); - pw.println(" Returns the user preferred display mode or null id no mode is set by user"); + + "REFRESH-RATE. If DISPLAY_ID is passed, the mode change is applied to display" + + "with id = DISPLAY_ID, else mode change is applied globally."); + pw.println(" clear-user-preferred-display-mode DISPLAY_ID (optional)"); + pw.println(" Clears the user preferred display mode. If DISPLAY_ID is passed, the mode" + + " is cleared for display with id = DISPLAY_ID, else mode is cleared globally."); + pw.println(" get-user-preferred-display-mode DISPLAY_ID (optional)"); + pw.println(" Returns the user preferred display mode or null if no mode is set by user." + + "If DISPLAY_ID is passed, the mode for display with id = DISPLAY_ID is " + + "returned, else global display mode is returned."); pw.println(" set-match-content-frame-rate-pref PREFERENCE"); pw.println(" Sets the match content frame rate preference as PREFERENCE "); pw.println(" get-match-content-frame-rate-pref"); @@ -235,28 +240,54 @@ class DisplayManagerShellCommand extends ShellCommand { getErrPrintWriter().println("Error: invalid format of width, height or refresh rate"); return 1; } - if (width < 0 || height < 0 || refreshRate <= 0.0f) { - getErrPrintWriter().println("Error: invalid value of width, height or refresh rate"); + if ((width < 0 || height < 0) && refreshRate <= 0.0f) { + getErrPrintWriter().println("Error: invalid value of resolution (width, height)" + + " and refresh rate"); return 1; } - final Context context = mService.getContext(); - final DisplayManager dm = context.getSystemService(DisplayManager.class); - dm.setUserPreferredDisplayMode(new Display.Mode(width, height, refreshRate)); + final String displayIdText = getNextArg(); + int displayId = Display.INVALID_DISPLAY; + if (displayIdText != null) { + try { + displayId = Integer.parseInt(displayIdText); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: invalid format of display ID"); + return 1; + } + } + mService.setUserPreferredDisplayModeInternal( + displayId, new Display.Mode(width, height, refreshRate)); return 0; } private int clearUserPreferredDisplayMode() { - final Context context = mService.getContext(); - final DisplayManager dm = context.getSystemService(DisplayManager.class); - dm.clearUserPreferredDisplayMode(); + final String displayIdText = getNextArg(); + int displayId = Display.INVALID_DISPLAY; + if (displayIdText != null) { + try { + displayId = Integer.parseInt(displayIdText); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: invalid format of display ID"); + return 1; + } + } + mService.setUserPreferredDisplayModeInternal(displayId, null); return 0; } private int getUserPreferredDisplayMode() { - final Context context = mService.getContext(); - final DisplayManager dm = context.getSystemService(DisplayManager.class); - final Display.Mode mode = dm.getUserPreferredDisplayMode(); + final String displayIdText = getNextArg(); + int displayId = Display.INVALID_DISPLAY; + if (displayIdText != null) { + try { + displayId = Integer.parseInt(displayIdText); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: invalid format of display ID"); + return 1; + } + } + final Display.Mode mode = mService.getUserPreferredDisplayModeInternal(displayId); if (mode == null) { getOutPrintWriter().println("User preferred display mode: null"); return 0; diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 300f59ee1dd4..84de8229f37b 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -847,6 +847,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { public void setUserPreferredDisplayModeLocked(Display.Mode mode) { final int oldModeId = getPreferredModeId(); mUserPreferredMode = mode; + if (mode != null && (mode.isRefreshRateSet() ^ mode.isResolutionSet())) { + mUserPreferredMode = findMode(mode.getPhysicalWidth(), + mode.getPhysicalHeight(), mode.getRefreshRate()); + } mUserPreferredModeId = findUserPreferredModeIdLocked(mode); if (oldModeId != getPreferredModeId()) { @@ -854,6 +858,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } + @Override + public Display.Mode getUserPreferredDisplayModeLocked() { + return mUserPreferredMode; + } + @Override public void setRequestedColorModeLocked(int colorMode) { requestColorModeLocked(colorMode); @@ -1062,6 +1071,18 @@ final class LocalDisplayAdapter extends DisplayAdapter { return matchingModeId; } + // Returns a mode with resolution (width, height) and/or refreshRate. If any one of the + // resolution or refresh-rate is valid, a mode having the valid parameters is returned. + private Display.Mode findMode(int width, int height, float refreshRate) { + for (int i = 0; i < mSupportedModes.size(); i++) { + Display.Mode supportedMode = mSupportedModes.valueAt(i).mMode; + if (supportedMode.matchesIfValid(width, height, refreshRate)) { + return supportedMode; + } + } + return null; + } + private int findUserPreferredModeIdLocked(Display.Mode userPreferredMode) { if (userPreferredMode != null) { for (int i = 0; i < mSupportedModes.size(); i++) { diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java index 4b0d43b3d1d4..2eba080e369e 100644 --- a/services/core/java/com/android/server/display/PersistentDataStore.java +++ b/services/core/java/com/android/server/display/PersistentDataStore.java @@ -291,6 +291,54 @@ final class PersistentDataStore { return false; } + public boolean setUserPreferredRefreshRate(DisplayDevice displayDevice, float refreshRate) { + final String displayDeviceUniqueId = displayDevice.getUniqueId(); + if (!displayDevice.hasStableUniqueId() || displayDeviceUniqueId == null) { + return false; + } + DisplayState state = getDisplayState(displayDevice.getUniqueId(), true); + if (state.setRefreshRate(refreshRate)) { + setDirty(); + return true; + } + return false; + } + + public float getUserPreferredRefreshRate(DisplayDevice device) { + if (device == null || !device.hasStableUniqueId()) { + return Float.NaN; + } + final DisplayState state = getDisplayState(device.getUniqueId(), false); + if (state == null) { + return Float.NaN; + } + return state.getRefreshRate(); + } + + public boolean setUserPreferredResolution(DisplayDevice displayDevice, int width, int height) { + final String displayDeviceUniqueId = displayDevice.getUniqueId(); + if (!displayDevice.hasStableUniqueId() || displayDeviceUniqueId == null) { + return false; + } + DisplayState state = getDisplayState(displayDevice.getUniqueId(), true); + if (state.setResolution(width, height)) { + setDirty(); + return true; + } + return false; + } + + public Point getUserPreferredResolution(DisplayDevice displayDevice) { + if (displayDevice == null || !displayDevice.hasStableUniqueId()) { + return null; + } + final DisplayState state = getDisplayState(displayDevice.getUniqueId(), false); + if (state == null) { + return null; + } + return state.getResolution(); + } + public Point getStableDisplaySize() { loadIfNeeded(); return mStableDeviceValues.getDisplaySize(); @@ -536,6 +584,9 @@ final class PersistentDataStore { private static final class DisplayState { private int mColorMode; private float mBrightness; + private int mWidth; + private int mHeight; + private float mRefreshRate; // Brightness configuration by user private BrightnessConfigurations mDisplayBrightnessConfigurations = @@ -576,6 +627,31 @@ final class PersistentDataStore { return mDisplayBrightnessConfigurations.mConfigurations.get(userSerial); } + public boolean setResolution(int width, int height) { + if (width == mWidth && height == mHeight) { + return false; + } + mWidth = width; + mHeight = height; + return true; + } + + public Point getResolution() { + return new Point(mWidth, mHeight); + } + + public boolean setRefreshRate(float refreshRate) { + if (refreshRate == mRefreshRate) { + return false; + } + mRefreshRate = refreshRate; + return true; + } + + public float getRefreshRate() { + return mRefreshRate; + } + public void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index b1cc51768754..c2c182c69569 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -4584,7 +4584,8 @@ public class StatsPullAtomService extends SystemService { int matchContentFrameRateUserPreference = displayManager.getMatchContentFrameRateUserPreference(); byte[] userDisabledHdrTypes = toBytes(displayManager.getUserDisabledHdrTypes()); - Display.Mode userPreferredDisplayMode = displayManager.getUserPreferredDisplayMode(); + Display.Mode userPreferredDisplayMode = + displayManager.getGlobalUserPreferredDisplayMode(); int userPreferredWidth = userPreferredDisplayMode != null ? userPreferredDisplayMode.getPhysicalWidth() : -1; int userPreferredHeight = userPreferredDisplayMode != null