diff options
10 files changed, 141 insertions, 18 deletions
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index a7748f4fae98..0ebb2a30685c 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -158,8 +158,6 @@ abstract class DisplayDevice { @Nullable public Point getDisplaySurfaceDefaultSizeLocked() { DisplayDeviceInfo displayDeviceInfo = getDisplayDeviceInfoLocked(); - final boolean isRotated = mCurrentOrientation == ROTATION_90 - || mCurrentOrientation == ROTATION_270; var width = displayDeviceInfo.width; var height = displayDeviceInfo.height; if (mIsAnisotropyCorrectionEnabled && displayDeviceInfo.yDpi > 0 @@ -170,7 +168,7 @@ abstract class DisplayDevice { width = (int) (width * displayDeviceInfo.yDpi / displayDeviceInfo.xDpi + 0.5); } } - return isRotated ? new Point(height, width) : new Point(width, height); + return isRotatedLocked() ? new Point(height, width) : new Point(width, height); } /** @@ -394,8 +392,7 @@ abstract class DisplayDevice { viewport.physicalFrame.setEmpty(); } - boolean isRotated = (mCurrentOrientation == Surface.ROTATION_90 - || mCurrentOrientation == ROTATION_270); + final boolean isRotated = isRotatedLocked(); DisplayDeviceInfo info = getDisplayDeviceInfoLocked(); viewport.deviceWidth = isRotated ? info.height : info.width; viewport.deviceHeight = isRotated ? info.width : info.height; @@ -425,6 +422,13 @@ abstract class DisplayDevice { pw.println("mCurrentSurface=" + mCurrentSurface); } + /** + * @return whether the orientation is {@link ROTATION_90} or {@link ROTATION_270}. + */ + boolean isRotatedLocked() { + return mCurrentOrientation == ROTATION_90 || mCurrentOrientation == ROTATION_270; + } + private DisplayDeviceConfig loadDisplayDeviceConfig() { return DisplayDeviceConfig.create(mContext, /* useConfigXml= */ false, mDisplayAdapter.getFeatureFlags()); diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 5eaaf3504e85..22e4bc502131 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -204,6 +204,13 @@ final class LogicalDisplay { new SparseArray<>(); /** + * If enabled, will not check for {@link Display#FLAG_ROTATES_WITH_CONTENT} in LogicalDisplay + * and simply use the {@link DisplayInfo#rotation} supplied by WindowManager via + * {@link #setDisplayInfoOverrideFromWindowManagerLocked} + */ + private boolean mAlwaysRotateDisplayDeviceEnabled; + + /** * If the aspect ratio of the resolution of the display does not match the physical aspect * ratio of the display, then without this feature enabled, picture would appear stretched to * the user. This is because applications assume that they are rendered on square pixels @@ -220,11 +227,11 @@ final class LogicalDisplay { private final boolean mIsAnisotropyCorrectionEnabled; LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) { - this(displayId, layerStack, primaryDisplayDevice, false); + this(displayId, layerStack, primaryDisplayDevice, false, false); } LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice, - boolean isAnisotropyCorrectionEnabled) { + boolean isAnisotropyCorrectionEnabled, boolean isAlwaysRotateDisplayDeviceEnabled) { mDisplayId = displayId; mLayerStack = layerStack; mPrimaryDisplayDevice = primaryDisplayDevice; @@ -236,6 +243,7 @@ final class LogicalDisplay { mPowerThrottlingDataId = DisplayDeviceConfig.DEFAULT_ID; mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId; mIsAnisotropyCorrectionEnabled = isAnisotropyCorrectionEnabled; + mAlwaysRotateDisplayDeviceEnabled = isAlwaysRotateDisplayDeviceEnabled; } public void setDevicePositionLocked(int position) { @@ -672,7 +680,12 @@ final class LogicalDisplay { // The orientation specifies how the physical coordinate system of the display // is rotated when the contents of the logical display are rendered. int orientation = Surface.ROTATION_0; - if ((displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0) { + + // FLAG_ROTATES_WITH_CONTENT is now handled in DisplayContent. When the flag + // mAlwaysRotateDisplayDeviceEnabled is removed, we should also remove this check for + // ROTATES_WITH_CONTENT here and always set the orientation. + if ((displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0 + || mAlwaysRotateDisplayDeviceEnabled) { orientation = displayInfo.rotation; } diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index e092fdae7cc7..21f90d4aeb94 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -1152,7 +1152,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) { final int layerStack = assignLayerStackLocked(displayId); final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device, - mFlags.isPixelAnisotropyCorrectionInLogicalDisplayEnabled()); + mFlags.isPixelAnisotropyCorrectionInLogicalDisplayEnabled(), + mFlags.isAlwaysRotateDisplayDeviceEnabled()); display.updateLocked(mDisplayDeviceRepo); final DisplayInfo info = display.getDisplayInfoLocked(); diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index ec5ad7de11b3..bcdb442c3ad3 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -366,7 +366,8 @@ public class VirtualDisplayAdapter extends DisplayAdapter { if (mSurface == null) { return null; } - return mSurface.getDefaultSize(); + final Point surfaceSize = mSurface.getDefaultSize(); + return isRotatedLocked() ? new Point(surfaceSize.y, surfaceSize.x) : surfaceSize; } @VisibleForTesting diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index 15ee9372b46a..5f455db39dd4 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -116,6 +116,10 @@ public class DisplayManagerFlags { Flags.FLAG_FAST_HDR_TRANSITIONS, Flags::fastHdrTransitions); + private final FlagState mAlwaysRotateDisplayDevice = new FlagState( + Flags.FLAG_ALWAYS_ROTATE_DISPLAY_DEVICE, + Flags::alwaysRotateDisplayDevice); + private final FlagState mRefreshRateVotingTelemetry = new FlagState( Flags.FLAG_REFRESH_RATE_VOTING_TELEMETRY, Flags::refreshRateVotingTelemetry @@ -260,6 +264,10 @@ public class DisplayManagerFlags { return mFastHdrTransitions.isEnabled(); } + public boolean isAlwaysRotateDisplayDeviceEnabled() { + return mAlwaysRotateDisplayDevice.isEnabled(); + } + public boolean isRefreshRateVotingTelemetryEnabled() { return mRefreshRateVotingTelemetry.isEnabled(); } @@ -298,6 +306,7 @@ public class DisplayManagerFlags { pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState); pw.println(" " + mAutoBrightnessModesFlagState); pw.println(" " + mFastHdrTransitions); + pw.println(" " + mAlwaysRotateDisplayDevice); pw.println(" " + mRefreshRateVotingTelemetry); pw.println(" " + mPixelAnisotropyCorrectionEnabled); pw.println(" " + mSensorBasedBrightnessThrottling); diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index 9bf36e4e605f..d2909b898704 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -178,6 +178,17 @@ flag { } flag { + name: "always_rotate_display_device" + namespace: "display_manager" + description: "Use rotation from WindowManager no matter whether FLAG_ROTATES_WITH_CONTENT is set or not" + bug: "302326003" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "refresh_rate_voting_telemetry" namespace: "display_manager" description: "Feature flag for enabling telemetry for refresh rate voting in DisplayManager" diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java index b616d24cfebb..802051660c0e 100644 --- a/services/core/java/com/android/server/wm/ContentRecorder.java +++ b/services/core/java/com/android/server/wm/ContentRecorder.java @@ -157,6 +157,10 @@ final class ContentRecorder implements WindowContainerListener { } } + void onMirrorOutputSurfaceOrientationChanged() { + onConfigurationChanged(mLastOrientation, mLastWindowingMode); + } + /** * Handle a configuration change on the display content, and resize recording if needed. * @param lastOrientation the prior orientation of the configuration diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 837d08b33756..dc643453c8dd 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -6835,6 +6835,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mContentRecorder; } + void onMirrorOutputSurfaceOrientationChanged() { + if (mContentRecorder != null) { + mContentRecorder.onMirrorOutputSurfaceOrientationChanged(); + } + } + /** * Pause the recording session. */ diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 384b91a07d4e..4a97128c27b6 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -19,6 +19,8 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.view.Display.TYPE_EXTERNAL; +import static android.view.Display.TYPE_OVERLAY; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; @@ -915,6 +917,11 @@ public class DisplayRotation { + " for " + mDisplayContent); userRotation = Surface.ROTATION_0; } + final int userRotationOverride = getUserRotationOverride(); + if (userRotationOverride != 0) { + userRotationMode = WindowManagerPolicy.USER_ROTATION_LOCKED; + userRotation = userRotationOverride; + } mUserRotationMode = userRotationMode; mUserRotation = userRotation; } @@ -965,6 +972,13 @@ public class DisplayRotation { if (changed) { mService.updateRotation(false /* alwaysSendConfiguration */, false /* forceRelayout */); + // ContentRecorder.onConfigurationChanged and Device.setProjectionLocked are called + // during updateRotation above. But onConfigurationChanged is called before + // Device.setProjectionLocked, which means that the onConfigurationChanged will + // not have the new rotation when it calls getDisplaySurfaceDefaultSize. + // To make sure that mirroring takes the new rotation of the output surface + // into account we need to call onConfigurationChanged again. + mDisplayContent.onMirrorOutputSurfaceOrientationChanged(); } } @@ -1780,6 +1794,23 @@ public class DisplayRotation { } } + @Surface.Rotation + private int getUserRotationOverride() { + final int userRotationOverride = SystemProperties.getInt("persist.demo.userrotation", + Surface.ROTATION_0); + if (userRotationOverride == Surface.ROTATION_0) { + return userRotationOverride; + } + + final var display = mDisplayContent.mDisplay; + if (display.getType() == TYPE_EXTERNAL || display.getType() == TYPE_OVERLAY) { + // TODO b/329442350 add chromecast virtual displays here + return userRotationOverride; + } + + return Surface.ROTATION_0; + } + @VisibleForTesting long uptimeMillis() { return SystemClock.uptimeMillis(); diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java index 549f0d74b67b..e798aa20f4bf 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java @@ -107,8 +107,7 @@ public class LogicalDisplayTest { @Test public void testLetterbox() { - mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, - /*isAnisotropyCorrectionEnabled=*/ false); + mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice); mDisplayDeviceInfo.xDpi = 0.5f; mDisplayDeviceInfo.yDpi = 1.0f; @@ -146,7 +145,8 @@ public class LogicalDisplayTest { @Test public void testNoLetterbox_anisotropyCorrection() { mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, - /*isAnisotropyCorrectionEnabled=*/ true); + /*isAnisotropyCorrectionEnabled=*/ true, + /*isAlwaysRotateDisplayDeviceEnabled=*/ true); // In case of Anisotropy of pixels, then the content should be rescaled so it would adjust // to using the whole screen. This is because display will rescale it back to fill the @@ -173,7 +173,8 @@ public class LogicalDisplayTest { @Test public void testLetterbox_anisotropyCorrectionYDpi() { mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, - /*isAnisotropyCorrectionEnabled=*/ true); + /*isAnisotropyCorrectionEnabled=*/ true, + /*isAlwaysRotateDisplayDeviceEnabled=*/ true); DisplayInfo displayInfo = new DisplayInfo(); displayInfo.logicalWidth = DISPLAY_WIDTH; @@ -191,8 +192,7 @@ public class LogicalDisplayTest { @Test public void testPillarbox() { - mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, - /*isAnisotropyCorrectionEnabled=*/ false); + mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice); mDisplayDeviceInfo.xDpi = 0.5f; mDisplayDeviceInfo.yDpi = 1.0f; @@ -230,7 +230,8 @@ public class LogicalDisplayTest { @Test public void testPillarbox_anisotropyCorrection() { mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, - /*isAnisotropyCorrectionEnabled=*/ true); + /*isAnisotropyCorrectionEnabled=*/ true, + /*isAlwaysRotateDisplayDeviceEnabled=*/ true); DisplayInfo displayInfo = new DisplayInfo(); displayInfo.logicalWidth = DISPLAY_WIDTH; @@ -257,7 +258,8 @@ public class LogicalDisplayTest { @Test public void testNoPillarbox_anisotropyCorrectionYDpi() { mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, - /*isAnisotropyCorrectionEnabled=*/ true); + /*isAnisotropyCorrectionEnabled=*/ true, + /*isAlwaysRotateDisplayDeviceEnabled=*/ true); // In case of Anisotropy of pixels, then the content should be rescaled so it would adjust // to using the whole screen. This is because display will rescale it back to fill the @@ -315,6 +317,47 @@ public class LogicalDisplayTest { } @Test + public void testGetDisplayPositionAlwaysRotateDisplayEnabled() { + mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, + /*isAnisotropyCorrectionEnabled=*/ true, + /*isAlwaysRotateDisplayDeviceEnabled=*/ true); + mLogicalDisplay.updateLocked(mDeviceRepo); + Point expectedPosition = new Point(); + + SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition()); + + expectedPosition.set(20, 40); + mLogicalDisplay.setDisplayOffsetsLocked(20, 40); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition()); + + DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = DISPLAY_WIDTH; + displayInfo.logicalHeight = DISPLAY_HEIGHT; + // Rotation sent from WindowManager is always taken into account by LogicalDisplay + // not matter whether FLAG_ROTATES_WITH_CONTENT is set or not. + // This is because WindowManager takes care of rotation and expects that LogicalDisplay + // will follow the rotation supplied by WindowManager + expectedPosition.set(115, -20); + displayInfo.rotation = Surface.ROTATION_90; + mLogicalDisplay.setDisplayInfoOverrideFromWindowManagerLocked(displayInfo); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition()); + + expectedPosition.set(40, -20); + mDisplayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; + mLogicalDisplay.updateLocked(mDeviceRepo); + displayInfo.logicalWidth = DISPLAY_HEIGHT; + displayInfo.logicalHeight = DISPLAY_WIDTH; + displayInfo.rotation = Surface.ROTATION_90; + mLogicalDisplay.setDisplayInfoOverrideFromWindowManagerLocked(displayInfo); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition()); + } + + @Test public void testDisplayInputFlags() { DisplayDevice displayDevice = new DisplayDevice(mDisplayAdapter, mDisplayToken, "unique_display_id", mContext) { |