From 45f6a2718adea9592a3ead1a9ef8055c7819b02e Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 3 Oct 2016 16:42:04 -0700 Subject: [PATCH 1/2] Prevent triggering orientation changes until previous completes. Currently we defer rotation while the screen rotation animation is animating, but there is an additional interval after the animation completes where the display may not yet be unfrozen and attempting to rotate again will not work. Bug: 31749456 Change-Id: I5f6830d85fb326d5032b597cbed1b767f0b627f5 --- .../java/com/android/server/wm/WindowManagerService.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ce0ad25ebcc6d..bd266968613de 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -6807,6 +6807,13 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress."); return false; } + if (mDisplayFrozen) { + // Even if the screen rotation animation has finished (e.g. isAnimating + // returns false), there is still some time where we haven't yet unfrozen + // the display. We also need to abort rotation here. + if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, still finishing previous rotation"); + return false; + } if (!mDisplayEnabled) { // No point choosing a rotation if the display is not enabled. From b14d4abc16ec7dd48737ab829e3f84b12017db7c Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 3 Oct 2016 18:13:33 -0700 Subject: [PATCH 2/2] Prevent any rotation while seamless rotation is pending. Various errors occur when using even the normal rotation animation while seamless rotation is pending. So we just defer the rotation like we do for the normal animation. Since we are doing this, we need to track when seamless rotation finishes so we can perform a post-rotate rotation if required. Bug: 31749456 Change-Id: I99f189306c690ce868496460e9ca7dcc95e4ccdc --- .../server/wm/WindowManagerService.java | 134 +++++++++++------- .../server/wm/WindowStateAnimator.java | 4 +- 2 files changed, 85 insertions(+), 53 deletions(-) diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index bd266968613de..7a73759f03886 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -654,6 +654,12 @@ public class WindowManagerService extends IWindowManager.Stub WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; SettingsObserver mSettingsObserver; + // A count of the windows which are 'seamlessly rotated', e.g. a surface + // at an old orientation is being transformed. We freeze orientation updates + // while any windows are seamlessly rotated, so we need to track when this + // hits zero so we can apply deferred orientation updates. + int mSeamlessRotationCount = 0; + private final class SettingsObserver extends ContentObserver { private final Uri mDisplayInversionEnabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); @@ -6821,62 +6827,20 @@ public class WindowManagerService extends IWindowManager.Stub return false; } - // TODO: Implement forced rotation changes. - // Set mAltOrientation to indicate that the application is receiving - // an orientation that has different metrics than it expected. - // eg. Portrait instead of Landscape. - - int rotation = mPolicy.rotationForOrientationLw(mLastOrientation, mRotation); - boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw( - mLastOrientation, rotation); - - if (DEBUG_ORIENTATION) { - Slog.v(TAG_WM, "Selected orientation " - + mLastOrientation + ", got rotation " + rotation - + " which has " + (altOrientation ? "incompatible" : "compatible") - + " metrics"); - } - - if (mRotation == rotation && mAltOrientation == altOrientation) { - // No change. - return false; - } - - if (DEBUG_ORIENTATION) { - Slog.v(TAG_WM, - "Rotation changed to " + rotation + (altOrientation ? " (alt)" : "") - + " from " + mRotation + (mAltOrientation ? " (alt)" : "") - + ", lastOrientation=" + mLastOrientation); - } - - int oldRotation = mRotation; - - mRotation = rotation; - mAltOrientation = altOrientation; - mPolicy.setRotationLw(mRotation); - - mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; - mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); - mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION); - mWaitingForConfig = true; final DisplayContent displayContent = getDefaultDisplayContentLocked(); - displayContent.layoutNeeded = true; - final int[] anim = new int[2]; - if (displayContent.isDimming()) { - anim[0] = anim[1] = 0; - } else { - mPolicy.selectRotationAnimationLw(anim); - } - boolean rotateSeamlessly = mPolicy.shouldRotateSeamlessly(oldRotation, mRotation); final WindowList windows = displayContent.getWindowList(); - // We can't rotate seamlessly while an existing seamless rotation is still - // waiting on windows to finish drawing. + + final int oldRotation = mRotation; + boolean rotateSeamlessly = mPolicy.shouldRotateSeamlessly(oldRotation, mRotation); + if (rotateSeamlessly) { for (int i = windows.size() - 1; i >= 0; i--) { WindowState w = windows.get(i); + // We can't rotate (seamlessly or not) while waiting for the last seamless rotation + // to complete (that is, waiting for windows to redraw). It's tempting to check + // w.mSeamlessRotationCount but that could be incorrect in the case of window-removal. if (w.mSeamlesslyRotated) { - rotateSeamlessly = false; - break; + return false; } // In what can only be called an unfortunate workaround we require // seamlessly rotated child windows to have the TRANSFORM_TO_DISPLAY_INVERSE @@ -6895,6 +6859,50 @@ public class WindowManagerService extends IWindowManager.Stub } } + // TODO: Implement forced rotation changes. + // Set mAltOrientation to indicate that the application is receiving + // an orientation that has different metrics than it expected. + // eg. Portrait instead of Landscape. + + int rotation = mPolicy.rotationForOrientationLw(mLastOrientation, mRotation); + boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw( + mLastOrientation, rotation); + + if (DEBUG_ORIENTATION) { + Slog.v(TAG_WM, "Selected orientation " + + mLastOrientation + ", got rotation " + rotation + + " which has " + (altOrientation ? "incompatible" : "compatible") + + " metrics"); + } + + if (mRotation == rotation && mAltOrientation == altOrientation) { + // No change. + return false; + } + + if (DEBUG_ORIENTATION) { + Slog.v(TAG_WM, + "Rotation changed to " + rotation + (altOrientation ? " (alt)" : "") + + " from " + mRotation + (mAltOrientation ? " (alt)" : "") + + ", lastOrientation=" + mLastOrientation); + } + + mRotation = rotation; + mAltOrientation = altOrientation; + mPolicy.setRotationLw(mRotation); + + mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; + mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); + mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION); + mWaitingForConfig = true; + displayContent.layoutNeeded = true; + final int[] anim = new int[2]; + if (displayContent.isDimming()) { + anim[0] = anim[1] = 0; + } else { + mPolicy.selectRotationAnimationLw(anim); + } + if (!rotateSeamlessly) { startFreezingDisplayLocked(inTransaction, anim[0], anim[1]); // startFreezingDisplayLocked can reset the ScreenRotationAnimation. @@ -6906,6 +6914,10 @@ public class WindowManagerService extends IWindowManager.Stub // When we are rotating seamlessly, we allow the elements to transition // to their rotated state independently and without a freeze required. screenRotationAnimation = null; + + // We have to reset this in case a window was removed before it + // finished seamless rotation. + mSeamlessRotationCount = 0; } // We need to update our screen size information to match the new rotation. If the rotation @@ -8921,8 +8933,8 @@ public class WindowManagerService extends IWindowManager.Stub if (w.mSeamlesslyRotated) { layoutNeeded = true; w.setDisplayLayoutNeeded(); + markForSeamlessRotation(w, false); } - w.mSeamlesslyRotated = false; } if (layoutNeeded) { mWindowPlacerLocked.performSurfacePlacement(); @@ -11508,6 +11520,26 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.registerShortcutKey(shortcutCode, shortcutKeyReceiver); } + void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) { + if (seamlesslyRotated == w.mSeamlesslyRotated) { + return; + } + w.mSeamlesslyRotated = seamlesslyRotated; + if (seamlesslyRotated) { + mSeamlessRotationCount++; + } else { + mSeamlessRotationCount--; + } + if (mSeamlessRotationCount == 0) { + if (DEBUG_ORIENTATION) { + Slog.i(TAG, "Performing post-rotate rotation after seamless rotation"); + } + if (updateRotationUncheckedLocked(false)) { + mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); + } + } + } + private final class LocalService extends WindowManagerInternal { @Override public void requestTraversalFromDisplayManager() { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 0285f7075e957..650dac91eb7f9 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1431,7 +1431,7 @@ class WindowStateAnimator { // If we are undergoing seamless rotation, the surface has already // been set up to persist at it's old location. We need to freeze // updates until a resize occurs. - w.mSeamlesslyRotated = w.mSeamlesslyRotated && !mSurfaceResized; + mService.markForSeamlessRotation(w, w.mSeamlesslyRotated && !mSurfaceResized); calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect); @@ -2156,7 +2156,7 @@ class WindowStateAnimator { cropRect.set(0, 0, w.mRequestedWidth, w.mRequestedWidth + w.mRequestedHeight); mSurfaceController.setCropInTransaction(cropRect, false); } else { - w.mSeamlesslyRotated = true; + mService.markForSeamlessRotation(w, true); transform.getValues(mService.mTmpFloats); float DsDx = mService.mTmpFloats[Matrix.MSCALE_X];