From 703719be15d4ab520caeecf9e13e6e801f29bd12 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 18 Apr 2018 17:53:15 -0700 Subject: [PATCH] Workaround to ensure we cancel the recents animation prior to starting home - It is possible for the call from SystemUI to cancel the recents animation to be processed and handled after the virtual key has been processed in PhoneWindowManager. This causes a misordering in which the canceling of the Recents animation clears the pending start activity remote animation (which is waiting for app transition ready). Instead, move the canceling of the Recents animation to PhoneWindowManager where the nav button is handled, to ensure that we cancel the animation on the same thread before we start the activity. Bug: 73188263 Test: Only able to reproduce so far artificially, which points to this misordering Change-Id: I1f3477acdf988953a5b3cef2e3b2b402af2d9909 Signed-off-by: Winson Chung --- .../android/app/ActivityManagerInternal.java | 5 +++++ .../statusbar/policy/KeyButtonView.java | 7 ------- .../server/am/ActivityManagerService.java | 5 +++++ .../server/policy/PhoneWindowManager.java | 8 ++++++++ .../server/wm/RecentsAnimationController.java | 2 +- .../server/wm/RemoteAnimationController.java | 18 +++++++++--------- 6 files changed, 28 insertions(+), 17 deletions(-) diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index db9d9234aeb86..97c9fa58622fb 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -376,6 +376,11 @@ public abstract class ActivityManagerInternal { */ public abstract boolean isRecentsComponentHomeActivity(int userId); + /** + * Cancels any currently running recents animation. + */ + public abstract void cancelRecentsAnimation(boolean restoreHomeStackPosition); + /** * Whether an UID is active or idle. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index 1b02e152ec11a..22a48f0b03094 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -269,13 +269,6 @@ public class KeyButtonView extends ImageView implements ButtonInterface { } if (mCode != 0) { if (doIt) { - // If there was a pending remote recents animation, then we need to - // cancel the animation now before we handle the button itself. In the case - // where we are going home and the recents animation has already started, - // just cancel the recents animation, leaving the home stack in place - boolean isHomeKey = mCode == KEYCODE_HOME; - ActivityManagerWrapper.getInstance().cancelRecentsAnimation(!isHomeKey); - sendEvent(KeyEvent.ACTION_UP, 0); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); } else { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e85351bfe489f..ad986a3879f63 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -26628,6 +26628,11 @@ public class ActivityManagerService extends IActivityManager.Stub return getRecentTasks().isRecentsComponentHomeActivity(userId); } + @Override + public void cancelRecentsAnimation(boolean restoreHomeStackPosition) { + ActivityManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition); + } + @Override public boolean isUidActive(int uid) { synchronized (ActivityManagerService.this) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 920e77f727714..bd4210c1b034b 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -6091,6 +6091,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { && (!isNavBarVirtKey || mNavBarVirtualKeyHapticFeedbackEnabled) && event.getRepeatCount() == 0; + // Cancel any pending remote recents animations before handling the button itself. In the + // case where we are going home and the recents animation has already started, just cancel + // the recents animation, leaving the home stack in place for the pending start activity + if (isNavBarVirtKey && !down) { + boolean isHomeKey = keyCode == KeyEvent.KEYCODE_HOME; + mActivityManagerInternal.cancelRecentsAnimation(!isHomeKey); + } + // Handle special keys. switch (keyCode) { case KeyEvent.KEYCODE_BACK: { diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 08fa153a3bad0..79b230d45110a 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -369,7 +369,7 @@ public class RecentsAnimationController implements DeathRecipient { } void cancelAnimation(@ReorderMode int reorderMode, String reason) { - if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation()"); + if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason); synchronized (mService.getWindowManagerLock()) { if (mCanceled) { // We've already canceled the animation diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 1b06b2fe1e7ea..67ef471031035 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -83,7 +83,8 @@ class RemoteAnimationController implements DeathRecipient { */ AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position, Rect stackBounds) { - if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token=" + appWindowToken); + if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token=" + + appWindowToken); final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper( appWindowToken, position, stackBounds); mPendingAnimations.add(adapter); @@ -96,8 +97,9 @@ class RemoteAnimationController implements DeathRecipient { void goodToGo() { if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo()"); if (mPendingAnimations.isEmpty() || mCanceled) { - if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished before good to go, canceled=" - + mCanceled + " mPendingAnimations=" + mPendingAnimations.size()); + if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished already," + + " canceled=" + mCanceled + + " mPendingAnimations=" + mPendingAnimations.size()); onAnimationFinished(); return; } @@ -123,10 +125,6 @@ class RemoteAnimationController implements DeathRecipient { } if (DEBUG_REMOTE_ANIMATIONS) { Slog.d(TAG, "startAnimation(): Notify animation start:"); - for (int i = 0; i < mPendingAnimations.size(); i++) { - Slog.d(TAG, "\t" + mPendingAnimations.get(i).mAppWindowToken); - } - } else if (DEBUG_APP_TRANSITIONS) { writeStartDebugStatement(); } }); @@ -166,7 +164,8 @@ class RemoteAnimationController implements DeathRecipient { if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tAdd token=" + wrapper.mAppWindowToken); targets.add(target); } else { - if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token=" + wrapper.mAppWindowToken); + if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token=" + + wrapper.mAppWindowToken); // We can't really start an animation but we still need to make sure to finish the // pending animation that was started by SurfaceAnimator @@ -188,7 +187,8 @@ class RemoteAnimationController implements DeathRecipient { releaseFinishedCallback(); mService.openSurfaceTransaction(); try { - if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): Notify animation finished:"); + if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, + "onAnimationFinished(): Notify animation finished:"); for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i); adapter.mCapturedFinishCallback.onAnimationFinished(adapter);