diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 15148a93cbe72..3571869ddf51b 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2730,6 +2730,7 @@ package android.view { method @NonNull public android.view.Display.Mode getDefaultMode(); method @NonNull public int[] getReportedHdrTypes(); method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut(); + method @Nullable public android.view.Display.Mode getSystemPreferredDisplayMode(); method public int getType(); method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode(); method public boolean hasAccess(int); diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 1a7a63ae8b693..af8ec279ac307 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -915,6 +915,17 @@ public final class DisplayManagerGlobal { } } + /** + * Returns the system preferred display mode. + */ + public Display.Mode getSystemPreferredDisplayMode(int displayId) { + try { + return mDm.getSystemPreferredDisplayMode(displayId); + } 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 diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 35663af189f40..b3af52b19063a 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -168,6 +168,7 @@ interface IDisplayManager { // Requires MODIFY_USER_PREFERRED_DISPLAY_MODE permission. void setUserPreferredDisplayMode(int displayId, in Mode mode); Mode getUserPreferredDisplayMode(int displayId); + Mode getSystemPreferredDisplayMode(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 fa39380cdcc13..246a8c9d17d3a 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -1117,6 +1117,19 @@ public final class Display { return mDisplayInfo.removeMode; } + /** + * Returns the system's preferred display mode. This mode will be used when the user has not + * specified a display-mode preference. This returns null if the boot display mode feature is + * not supported by system. + * + * @hide + */ + @TestApi + @Nullable + public Display.Mode getSystemPreferredDisplayMode() { + return mGlobal.getSystemPreferredDisplayMode(getDisplayId()); + } + /** * Returns the display's HDR capabilities. * diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 904d7c82959b7..6f5fea258119b 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -197,6 +197,9 @@ public final class SurfaceControl implements Parcelable { private static native int[] nativeGetCompositionDataspaces(); private static native boolean nativeSetActiveColorMode(IBinder displayToken, int colorMode); + private static native boolean nativeGetBootDisplayModeSupport(); + private static native void nativeSetBootDisplayMode(IBinder displayToken, int displayMode); + private static native void nativeClearBootDisplayMode(IBinder displayToken); private static native void nativeSetAutoLowLatencyMode(IBinder displayToken, boolean on); private static native void nativeSetGameContentType(IBinder displayToken, boolean on); private static native void nativeSetDisplayPowerMode( @@ -1878,6 +1881,8 @@ public final class SurfaceControl implements Parcelable { public boolean autoLowLatencyModeSupported; public boolean gameContentTypeSupported; + public int preferredBootDisplayMode; + @Override public String toString() { return "DynamicDisplayInfo{" @@ -1887,7 +1892,8 @@ public final class SurfaceControl implements Parcelable { + ", activeColorMode=" + activeColorMode + ", hdrCapabilities=" + hdrCapabilities + ", autoLowLatencyModeSupported=" + autoLowLatencyModeSupported - + ", gameContentTypeSupported" + gameContentTypeSupported + "}"; + + ", gameContentTypeSupported" + gameContentTypeSupported + + ", preferredBootDisplayMode" + preferredBootDisplayMode + "}"; } @Override @@ -1899,7 +1905,8 @@ public final class SurfaceControl implements Parcelable { && activeDisplayModeId == that.activeDisplayModeId && Arrays.equals(supportedColorModes, that.supportedColorModes) && activeColorMode == that.activeColorMode - && Objects.equals(hdrCapabilities, that.hdrCapabilities); + && Objects.equals(hdrCapabilities, that.hdrCapabilities) + && preferredBootDisplayMode == that.preferredBootDisplayMode; } @Override @@ -2263,6 +2270,36 @@ public final class SurfaceControl implements Parcelable { return colorSpaces; } + /** + * @hide + */ + public static boolean getBootDisplayModeSupport() { + return nativeGetBootDisplayModeSupport(); + } + + /** There is no associated getter for this method. When this is set, the display is expected + * to start up in this mode next time the device reboots. + * @hide + */ + public static void setBootDisplayMode(IBinder displayToken, int displayModeId) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + + nativeSetBootDisplayMode(displayToken, displayModeId); + } + + /** + * @hide + */ + public static void clearBootDisplayMode(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + + nativeClearBootDisplayMode(displayToken); + } + /** * @hide */ diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index a8cf2536341ba..4d30874188b28 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1638,6 +1638,27 @@ static void nativeOverrideHdrTypes(JNIEnv* env, jclass clazz, jobject tokenObjec } } +static jboolean nativeGetBootDisplayModeSupport(JNIEnv* env, jclass clazz) { + bool isBootDisplayModeSupported = false; + SurfaceComposerClient::getBootDisplayModeSupport(&isBootDisplayModeSupported); + return static_cast(isBootDisplayModeSupported); +} + +static void nativeSetBootDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObject, + jint displayModId) { + sp token(ibinderForJavaObject(env, tokenObject)); + if (token == NULL) return; + + SurfaceComposerClient::setBootDisplayMode(token, displayModId); +} + +static void nativeClearBootDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObject) { + sp token(ibinderForJavaObject(env, tokenObject)); + if (token == NULL) return; + + SurfaceComposerClient::clearBootDisplayMode(token); +} + static void nativeSetAutoLowLatencyMode(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) { sp token(ibinderForJavaObject(env, tokenObject)); if (token == NULL) return; @@ -2046,6 +2067,12 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetDisplayNativePrimaries }, {"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z", (void*)nativeSetActiveColorMode}, + {"nativeGetBootDisplayModeSupport", "()Z", + (void*)nativeGetBootDisplayModeSupport }, + {"nativeSetBootDisplayMode", "(Landroid/os/IBinder;I)V", + (void*)nativeSetBootDisplayMode }, + {"nativeClearBootDisplayMode", "(Landroid/os/IBinder;)V", + (void*)nativeClearBootDisplayMode }, {"nativeSetAutoLowLatencyMode", "(Landroid/os/IBinder;Z)V", (void*)nativeSetAutoLowLatencyMode }, {"nativeSetGameContentType", "(Landroid/os/IBinder;Z)V", diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index d0ce9ef47c350..5de162ce711c4 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -221,6 +221,22 @@ abstract class DisplayDevice { return EMPTY_DISPLAY_MODE; } + /** + * Returns the system preferred display mode. + */ + public Display.Mode getSystemPreferredDisplayModeLocked() { + return EMPTY_DISPLAY_MODE; + } + + /** + * Returns the display mode that was being used when this display was first found by + * display manager. + * @hide + */ + public Display.Mode getActiveDisplayModeAtStartLocked() { + 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 4e88acd11facf..7f1482e6dde1b 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1833,6 +1833,16 @@ public final class DisplayManagerService extends SystemService { } } + Display.Mode getSystemPreferredDisplayModeInternal(int displayId) { + synchronized (mSyncRoot) { + final DisplayDevice device = getDeviceForDisplayLocked(displayId); + if (device == null) { + return null; + } + return device.getSystemPreferredDisplayModeLocked(); + } + } + void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) { mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled); } @@ -2183,6 +2193,16 @@ public final class DisplayManagerService extends SystemService { } } + Display.Mode getActiveDisplayModeAtStart(int displayId) { + synchronized (mSyncRoot) { + final DisplayDevice device = getDeviceForDisplayLocked(displayId); + if (device == null) { + return null; + } + return device.getActiveDisplayModeAtStartLocked(); + } + } + void setAmbientColorTemperatureOverride(float cct) { synchronized (mSyncRoot) { final DisplayPowerController displayPowerController = mDisplayPowerControllers.get( @@ -3470,6 +3490,16 @@ public final class DisplayManagerService extends SystemService { } } + @Override // Binder call + public Display.Mode getSystemPreferredDisplayMode(int displayId) { + final long token = Binder.clearCallingIdentity(); + try { + return getSystemPreferredDisplayModeInternal(displayId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override // Binder call public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) { mContext.enforceCallingOrSelfPermission( diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index a9875c873bbb1..bfdac5781b752 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -68,6 +68,8 @@ class DisplayManagerShellCommand extends ShellCommand { return clearUserPreferredDisplayMode(); case "get-user-preferred-display-mode": return getUserPreferredDisplayMode(); + case "get-active-display-mode-at-start": + return getActiveDisplayModeAtStart(); case "set-match-content-frame-rate-pref": return setMatchContentFrameRateUserPreference(); case "get-match-content-frame-rate-pref": @@ -125,6 +127,9 @@ class DisplayManagerShellCommand extends ShellCommand { 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(" get-active-display-mode-at-start DISPLAY_ID"); + pw.println(" Returns the display mode which was found at boot time of display with " + + "id = DISPLAY_ID"); 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"); @@ -298,6 +303,30 @@ class DisplayManagerShellCommand extends ShellCommand { return 0; } + private int getActiveDisplayModeAtStart() { + final String displayIdText = getNextArg(); + if (displayIdText == null) { + getErrPrintWriter().println("Error: no displayId specified"); + return 1; + } + final int displayId; + try { + displayId = Integer.parseInt(displayIdText); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: invalid displayId"); + return 1; + } + + Display.Mode mode = mService.getActiveDisplayModeAtStart(displayId); + if (mode == null) { + getOutPrintWriter().println("Boot display mode: null"); + return 0; + } + getOutPrintWriter().println("Boot display mode: " + mode.getPhysicalWidth() + " " + + mode.getPhysicalHeight() + " " + mode.getRefreshRate()); + return 0; + } + private int setMatchContentFrameRateUserPreference() { final String matchContentFrameRatePrefText = getNextArg(); if (matchContentFrameRatePrefText == null) { diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 3a9ef0a83f6bd..a31c2314bd1ff 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -192,8 +192,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; private float mSdrBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; private int mDefaultModeId = INVALID_MODE_ID; + private int mSystemPreferredModeId = INVALID_MODE_ID; private int mDefaultModeGroup; private int mUserPreferredModeId = INVALID_MODE_ID; + // This is used only for the purpose of testing, to verify if the mode was correct when the + // device started or booted. + private int mActiveDisplayModeAtStartId = INVALID_MODE_ID; private Display.Mode mUserPreferredMode; private int mActiveModeId = INVALID_MODE_ID; private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs = @@ -208,7 +212,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private boolean mSidekickActive; private SidekickInternal mSidekickInternal; private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo; - // The supported display modes according in SurfaceFlinger + // The supported display modes according to SurfaceFlinger private SurfaceControl.DisplayMode[] mSfDisplayModes; // The active display mode in SurfaceFlinger private SurfaceControl.DisplayMode mActiveSfDisplayMode; @@ -230,6 +234,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay, mSurfaceControlProxy); mDisplayDeviceConfig = null; + mActiveDisplayModeAtStartId = dynamicInfo.activeDisplayModeId; } @Override @@ -237,13 +242,24 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + /** + * Returns the boot display mode of this display. + * @hide + */ + @Override + public Display.Mode getActiveDisplayModeAtStartLocked() { + return findMode(mActiveDisplayModeAtStartId); + } + /** * Returns true if there is a change. **/ public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo, SurfaceControl.DynamicDisplayInfo dynamicInfo, SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { - boolean changed = updateDisplayModesLocked( + boolean changed = + updateSystemPreferredDisplayMode(dynamicInfo.preferredBootDisplayMode); + changed |= updateDisplayModesLocked( dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs); changed |= updateStaticInfo(staticInfo); changed |= updateColorModesLocked(dynamicInfo.supportedColorModes, @@ -369,8 +385,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { // For a new display, we need to initialize the default mode ID. if (mDefaultModeId == INVALID_MODE_ID) { - mDefaultModeId = activeRecord.mMode.getModeId(); - mDefaultModeGroup = mActiveSfDisplayMode.group; + mDefaultModeId = mSystemPreferredModeId != INVALID_MODE_ID + ? mSystemPreferredModeId : activeRecord.mMode.getModeId(); + mDefaultModeGroup = mSystemPreferredModeId != INVALID_MODE_ID + ? getModeById(mSfDisplayModes, mSystemPreferredModeId).group + : mActiveSfDisplayMode.group; } else if (modesAdded && activeModeChanged) { Slog.d(TAG, "New display modes are added and the active mode has changed, " + "use active mode as default mode."); @@ -531,6 +550,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + private boolean updateSystemPreferredDisplayMode(int modeId) { + if (!mSurfaceControlProxy.getBootDisplayModeSupport() + || mSystemPreferredModeId == modeId) { + return false; + } + mSystemPreferredModeId = modeId; + return true; + } + private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes, int modeId) { for (SurfaceControl.DisplayMode mode : supportedModes) { @@ -857,6 +885,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { if (oldModeId != getPreferredModeId()) { updateDeviceInfoLocked(); } + + if (!mSurfaceControlProxy.getBootDisplayModeSupport()) { + return; + } + if (mUserPreferredMode == null) { + mSurfaceControlProxy.clearBootDisplayMode(getDisplayTokenLocked()); + } else { + mSurfaceControlProxy.setBootDisplayMode(getDisplayTokenLocked(), + mUserPreferredMode.getModeId()); + } } @Override @@ -864,6 +902,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { return mUserPreferredMode; } + @Override + public Display.Mode getSystemPreferredDisplayModeLocked() { + return findMode(mSystemPreferredModeId); + } + @Override public void setRequestedColorModeLocked(int colorMode) { requestColorModeLocked(colorMode); @@ -1072,6 +1115,17 @@ final class LocalDisplayAdapter extends DisplayAdapter { return matchingModeId; } + // Returns a mode with id = modeId. + private Display.Mode findMode(int modeId) { + for (int i = 0; i < mSupportedModes.size(); i++) { + Display.Mode supportedMode = mSupportedModes.valueAt(i).mMode; + if (supportedMode.getModeId() == modeId) { + return supportedMode; + } + } + return null; + } + // 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) { @@ -1318,6 +1372,18 @@ final class LocalDisplayAdapter extends DisplayAdapter { return SurfaceControl.setActiveColorMode(displayToken, colorMode); } + public boolean getBootDisplayModeSupport() { + return SurfaceControl.getBootDisplayModeSupport(); + } + + public void setBootDisplayMode(IBinder displayToken, int modeId) { + SurfaceControl.setBootDisplayMode(displayToken, modeId); + } + + public void clearBootDisplayMode(IBinder displayToken) { + SurfaceControl.clearBootDisplayMode(displayToken); + } + public void setAutoLowLatencyMode(IBinder displayToken, boolean on) { SurfaceControl.setAutoLowLatencyMode(displayToken, on); @@ -1340,7 +1406,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { return SurfaceControl.setDisplayBrightness(displayToken, sdrBacklight, sdrNits, displayBacklight, displayNits); } - } static class BacklightAdapter {