From 42769fff6ffe3369333ff8366f7eede5da9f2637 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Tue, 20 Sep 2016 13:36:42 -0700 Subject: [PATCH] Fix animation glitch with overlapping orientation changes. Imagine the following scenario: 1. We want to begin a new app transition to an app which will change the orientation. 2. A rotation animation from a previous rotation change is in progress. 3. Windows have finished drawing the first orientation change, so the display is already unfrozen. In this case, we won't have a chance to select the new orientation which our pending transition requires, because updateRotationUncheckedLocked will bail due to the existing animation being in progress. This causes the app behind to be revealed in the incorrect orientation (sometimes creating landscape launchers) and the transition to be interrupted halfway by the screenshot. In this case we can just defer the app transition until the rotation animation has had a chance to complete. Bug: 31098404 Change-Id: I1ae1041d3018d5681b46cd39c2d582861f0b014e --- .../android/server/wm/WindowManagerService.java | 13 +++++++++++++ .../android/server/wm/WindowSurfacePlacer.java | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 707b13780c460..91daf99c9ee27 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3859,6 +3859,19 @@ public class WindowManagerService extends IWindowManager.Stub } } + // If this is true we have updated our desired orientation, but not yet + // changed the real orientation our applied our screen rotation animation. + // For example, because a previous screen rotation was in progress. + boolean rotationNeedsUpdateLocked() { + int rotation = mPolicy.rotationForOrientationLw(mLastOrientation, mRotation); + boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw( + mLastOrientation, rotation); + if (mRotation == rotation && mAltOrientation == altOrientation) { + return false; + } + return true; + } + @Override public int[] setNewConfiguration(Configuration config) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 5a79da938c605..37dcb2168694c 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -1353,8 +1353,25 @@ class WindowSurfacePlacer { "Checking " + appsCount + " opening apps (frozen=" + mService.mDisplayFrozen + " timeout=" + mService.mAppTransition.isTimeout() + ")..."); + final ScreenRotationAnimation screenRotationAnimation = + mService.mAnimator.getScreenRotationAnimationLocked( + Display.DEFAULT_DISPLAY); + int reason = APP_TRANSITION_TIMEOUT; if (!mService.mAppTransition.isTimeout()) { + // Imagine the case where we are changing orientation due to an app transition, but a previous + // orientation change is still in progress. We won't process the orientation change + // for our transition because we need to wait for the rotation animation to finish. + // If we start the app transition at this point, we will interrupt it halfway with a new rotation + // animation after the old one finally finishes. It's better to defer the + // app transition. + if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() && + mService.rotationNeedsUpdateLocked()) { + if (DEBUG_APP_TRANSITIONS) { + Slog.v(TAG, "Delaying app transition for screen rotation animation to finish"); + } + return false; + } for (int i = 0; i < appsCount; i++) { AppWindowToken wtoken = mService.mOpeningApps.valueAt(i); if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,