diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 66250fcebe926..d3c99b88fcfce 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1147,15 +1147,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 @@ -2714,11 +2714,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 @@ -2733,6 +2736,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 e3690e55f4da5..01604e6becf03 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 de5c9adbc599a..89ac8bf2d7bcb 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 bf6e6651741c8..1a7a63ae8b693 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -896,9 +896,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(); } @@ -907,9 +907,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 d38d388ca8a30..35663af189f40 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 3cc51c7e8b51d..70266c1717a1f 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 def9685b1c1f0..d0ce9ef47c350 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 c558f91a8b3fb..3feffc6ae7dfc 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -873,11 +873,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; } @@ -1560,8 +1560,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; @@ -1688,6 +1691,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. @@ -1731,36 +1752,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(); } } @@ -2373,7 +2437,7 @@ public final class DisplayManagerService extends SystemService { pw.println(" mMinimumBrightnessCurve=" + mMinimumBrightnessCurve); if (mUserPreferredMode != null) { - pw.println(mUserPreferredMode.toString()); + pw.println(mUserPreferredMode); } pw.println(); @@ -3379,23 +3443,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 9a7ddcb2ff913..a9a1f08c140a2 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 300f59ee1dd42..84de8229f37b1 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 4b0d43b3d1d4f..2eba080e369ee 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 f0bf86fe0628a..07a584913b0bc 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -4588,7 +4588,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