From da90308633e4dec3a764a05af71b1665e789b840 Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Wed, 19 Oct 2022 18:17:38 +0800 Subject: [PATCH] Generalize back animation and transition sequence... for both shell and legacy transition system. The general sequence will be: 1. Create animation leash in core, pass to remote animation runner. 2. Remote animation finish, collect finish transaction before transition happen. Apply finish transaction here if back event won't trigger. 3. Check whether next transition is triggered from back gesture via checking the open/close targets. The 3rd step introduce another change, which to let the back transition happen. Instead of consume next transition, we should try to find out whether the participant was the animation target, because we cannot assume what the opening target will do in resume stage. For example, the opening activity could finish itself, or start another activity at onResume, so in either case, the opening activity will become closing activity. We can only ensure that activity will participant in next transition. So the more robust way should be, find out those targets which were animated. For shell transition, and add a flag to the corresponding change, then the transition handler can determine what to do based on the targets. For legacy transition, ignore those targets when estimate the transition type in AppTransitionController#getTransitCompatType. Bug: 238474994 Bug: 131727607 Test: varify transition can be handled for some unexpected scenario: 1. launch new activity when close app. 2. launch singleInstance activity when close app. Test: do back gesture on both shell/legacy transiton system, verify no flicker happen whenever the back is triggered. Change-Id: I9364be25c608f7b5797577d26b57bc7d6f2dde9c --- .../android/window/BackNavigationInfo.java | 36 +-- core/java/android/window/TransitionInfo.java | 9 +- data/etc/services.core.protolog.json | 12 - .../shell/back/BackAnimationController.java | 48 +-- .../wm/shell/back/BackTransitionHandler.java | 78 ----- .../wm/shell/dagger/WMShellBaseModule.java | 5 +- .../transition/DefaultTransitionHandler.java | 6 + .../back/BackAnimationControllerTest.java | 8 +- .../server/wm/AppTransitionController.java | 33 +- .../server/wm/BackNavigationController.java | 290 +++++++++++++++--- .../core/java/com/android/server/wm/Task.java | 15 - .../com/android/server/wm/Transition.java | 27 +- 12 files changed, 324 insertions(+), 243 deletions(-) delete mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java index 9b91cf2e9db62..a25e0351df839 100644 --- a/core/java/android/window/BackNavigationInfo.java +++ b/core/java/android/window/BackNavigationInfo.java @@ -89,8 +89,6 @@ public final class BackNavigationInfo implements Parcelable { @Nullable private final IOnBackInvokedCallback mOnBackInvokedCallback; private final boolean mPrepareRemoteAnimation; - @Nullable - private WindowContainerToken mDepartingWindowContainerToken; /** * Create a new {@link BackNavigationInfo} instance. @@ -100,20 +98,15 @@ public final class BackNavigationInfo implements Parcelable { * back preview. * @param onBackInvokedCallback The back callback registered by the current top level window. * @param departingWindowContainerToken The {@link WindowContainerToken} of departing window. - * @param isPrepareRemoteAnimation Return whether the core is preparing a back gesture - * animation, if true, the caller of startBackNavigation should - * be expected to receive an animation start callback. */ private BackNavigationInfo(@BackTargetType int type, @Nullable RemoteCallback onBackNavigationDone, @Nullable IOnBackInvokedCallback onBackInvokedCallback, - boolean isPrepareRemoteAnimation, - @Nullable WindowContainerToken departingWindowContainerToken) { + boolean isPrepareRemoteAnimation) { mType = type; mOnBackNavigationDone = onBackNavigationDone; mOnBackInvokedCallback = onBackInvokedCallback; mPrepareRemoteAnimation = isPrepareRemoteAnimation; - mDepartingWindowContainerToken = departingWindowContainerToken; } private BackNavigationInfo(@NonNull Parcel in) { @@ -121,7 +114,6 @@ public final class BackNavigationInfo implements Parcelable { mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR); mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder()); mPrepareRemoteAnimation = in.readBoolean(); - mDepartingWindowContainerToken = in.readTypedObject(WindowContainerToken.CREATOR); } @Override @@ -130,7 +122,6 @@ public final class BackNavigationInfo implements Parcelable { dest.writeTypedObject(mOnBackNavigationDone, flags); dest.writeStrongInterface(mOnBackInvokedCallback); dest.writeBoolean(mPrepareRemoteAnimation); - dest.writeTypedObject(mDepartingWindowContainerToken, flags); } /** @@ -163,18 +154,6 @@ public final class BackNavigationInfo implements Parcelable { return mPrepareRemoteAnimation; } - /** - * Returns the {@link WindowContainerToken} of the highest container in the hierarchy being - * removed. - *

- * For example, if an Activity is the last one of its Task, the Task's token will be given. - * Otherwise, it will be the Activity's token. - */ - @Nullable - public WindowContainerToken getDepartingWindowContainerToken() { - return mDepartingWindowContainerToken; - } - /** * Callback to be called when the back preview is finished in order to notify the server that * it can clean up the resources created for the animation. @@ -212,7 +191,6 @@ public final class BackNavigationInfo implements Parcelable { + "mType=" + typeToString(mType) + " (" + mType + ")" + ", mOnBackNavigationDone=" + mOnBackNavigationDone + ", mOnBackInvokedCallback=" + mOnBackInvokedCallback - + ", mWindowContainerToken=" + mDepartingWindowContainerToken + '}'; } @@ -248,8 +226,6 @@ public final class BackNavigationInfo implements Parcelable { @Nullable private IOnBackInvokedCallback mOnBackInvokedCallback = null; private boolean mPrepareRemoteAnimation; - @Nullable - private WindowContainerToken mDepartingWindowContainerToken = null; /** * @see BackNavigationInfo#getType() @@ -284,21 +260,13 @@ public final class BackNavigationInfo implements Parcelable { return this; } - /** - * @see BackNavigationInfo#getDepartingWindowContainerToken() - */ - public void setDepartingWCT(@NonNull WindowContainerToken windowContainerToken) { - mDepartingWindowContainerToken = windowContainerToken; - } - /** * Builds and returns an instance of {@link BackNavigationInfo} */ public BackNavigationInfo build() { return new BackNavigationInfo(mType, mOnBackNavigationDone, mOnBackInvokedCallback, - mPrepareRemoteAnimation, - mDepartingWindowContainerToken); + mPrepareRemoteAnimation); } } } diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 0956a71bd92d7..c2da638aca8d6 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -138,8 +138,11 @@ public final class TransitionInfo implements Parcelable { /** The container is a system window, excluding wallpaper and input-method. */ public static final int FLAG_IS_SYSTEM_WINDOW = 1 << 16; + /** The window was animated by back gesture. */ + public static final int FLAG_BACK_GESTURE_ANIMATED = 1 << 17; + /** The first unused bit. This can be used by remotes to attach custom flags to this change. */ - public static final int FLAG_FIRST_CUSTOM = 1 << 17; + public static final int FLAG_FIRST_CUSTOM = 1 << 18; /** The change belongs to a window that won't contain activities. */ public static final int FLAGS_IS_NON_APP_WINDOW = @@ -165,6 +168,7 @@ public final class TransitionInfo implements Parcelable { FLAG_IS_BEHIND_STARTING_WINDOW, FLAG_IS_OCCLUDED, FLAG_IS_SYSTEM_WINDOW, + FLAG_BACK_GESTURE_ANIMATED, FLAG_FIRST_CUSTOM }) public @interface ChangeFlags {} @@ -380,6 +384,9 @@ public final class TransitionInfo implements Parcelable { if ((flags & FLAG_IS_SYSTEM_WINDOW) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("FLAG_IS_SYSTEM_WINDOW"); } + if ((flags & FLAG_BACK_GESTURE_ANIMATED) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("FLAG_BACK_GESTURE_ANIMATED"); + } if ((flags & FLAG_FIRST_CUSTOM) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM"); } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 85c6beb29883b..af07999a88883 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -2173,12 +2173,6 @@ "group": "WM_DEBUG_ANIM", "at": "com\/android\/server\/wm\/WindowContainer.java" }, - "-23020844": { - "message": "Back: Reset surfaces", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "-21399771": { "message": "activity %s already destroying, skipping request with reason:%s", "level": "VERBOSE", @@ -3817,12 +3811,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "1544805551": { - "message": "Skipping app transition animation. task=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/Task.java" - }, "1557732761": { "message": "For Intent %s bringing to top: %s", "level": "DEBUG", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 64220c82fd9a2..b6327e5d91f51 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -62,7 +62,6 @@ import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; -import com.android.wm.shell.transition.Transitions; import java.util.concurrent.atomic.AtomicBoolean; @@ -82,8 +81,6 @@ public class BackAnimationController implements RemoteCallable mAnimationDefinition = new SparseArray<>(); - private final Transitions mTransitions; - private BackTransitionHandler mBackTransitionHandler; @VisibleForTesting final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() { @@ -148,11 +143,9 @@ public class BackAnimationController implements RemoteCallable { - finishBackNavigation(); - finishCallback.onTransitionFinished(null, null); - }); - } - private void createAdapter() { IBackAnimationRunner runner = new IBackAnimationRunner.Stub() { @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java deleted file mode 100644 index 6d72d9c1f6375..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.back; - -import android.os.IBinder; -import android.view.SurfaceControl; -import android.window.TransitionInfo; -import android.window.TransitionRequestInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.wm.shell.transition.Transitions; - -class BackTransitionHandler implements Transitions.TransitionHandler { - private BackAnimationController mBackAnimationController; - private WindowContainerToken mDepartingWindowContainerToken; - - BackTransitionHandler(@NonNull BackAnimationController backAnimationController) { - mBackAnimationController = backAnimationController; - } - - @Override - public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (mDepartingWindowContainerToken != null) { - final TransitionInfo.Change change = info.getChange(mDepartingWindowContainerToken); - if (change == null) { - return false; - } - - startTransaction.hide(change.getLeash()); - startTransaction.apply(); - mDepartingWindowContainerToken = null; - mBackAnimationController.finishTransition(finishCallback); - return true; - } - - return false; - } - - @Nullable - @Override - public WindowContainerTransaction handleRequest(@NonNull IBinder transition, - @NonNull TransitionRequestInfo request) { - return null; - } - - @Override - public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - } - - void setDepartingWindowContainerToken( - @Nullable WindowContainerToken departingWindowContainerToken) { - mDepartingWindowContainerToken = departingWindowContainerToken; - } -} - diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 1977dcb81e97e..962be9da2111b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -260,13 +260,12 @@ public abstract class WMShellBaseModule { ShellInit shellInit, ShellController shellController, @ShellMainThread ShellExecutor shellExecutor, - @ShellBackgroundThread Handler backgroundHandler, - Transitions transitions + @ShellBackgroundThread Handler backgroundHandler ) { if (BackAnimationController.IS_ENABLED) { return Optional.of( new BackAnimationController(shellInit, shellController, shellExecutor, - backgroundHandler, context, transitions)); + backgroundHandler, context)); } return Optional.empty(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 928e71f8d3a6c..63d4a6f5acd91 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -41,6 +41,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_RELAUNCH; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL; import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL; import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS; @@ -395,6 +396,11 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } } + // The back gesture has animated this change before transition happen, so here we don't + // play the animation again. + if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { + continue; + } // Don't animate anything that isn't independent. if (!TransitionInfo.isIndependent(change, info)) continue; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index 7896247c5f5af..7eccbf42e67dc 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -64,7 +64,6 @@ import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.sysui.ShellSharedConstants; -import com.android.wm.shell.transition.Transitions; import org.junit.Before; import org.junit.Rule; @@ -102,9 +101,6 @@ public class BackAnimationControllerTest extends ShellTestCase { @Mock private IRemoteAnimationRunner mBackAnimationRunner; - @Mock - private Transitions mTransitions; - @Mock private ShellController mShellController; @@ -127,7 +123,7 @@ public class BackAnimationControllerTest extends ShellTestCase { mController = new BackAnimationController(mShellInit, mShellController, mShellExecutor, new Handler(mTestableLooper.getLooper()), mActivityTaskManager, mContext, - mContentResolver, mTransitions); + mContentResolver); mController.setEnableUAnimation(true); mShellInit.init(); mEventTime = 0; @@ -225,7 +221,7 @@ public class BackAnimationControllerTest extends ShellTestCase { mController = new BackAnimationController(shellInit, mShellController, mShellExecutor, new Handler(mTestableLooper.getLooper()), mActivityTaskManager, mContext, - mContentResolver, mTransitions); + mContentResolver); shellInit.init(); mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 9011533001691..bd22b32742a15 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -215,9 +215,20 @@ public class AppTransitionController { mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded( mDisplayContent.mOpeningApps); + ArraySet tmpOpenApps = mDisplayContent.mOpeningApps; + ArraySet tmpCloseApps = mDisplayContent.mClosingApps; + if (mDisplayContent.mAtmService.mBackNavigationController.isWaitBackTransition()) { + tmpOpenApps = new ArraySet<>(mDisplayContent.mOpeningApps); + tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps); + if (mDisplayContent.mAtmService.mBackNavigationController + .removeIfContainsBackAnimationTargets(tmpOpenApps, tmpCloseApps)) { + mDisplayContent.mAtmService.mBackNavigationController.clearBackAnimations(null); + } + } + @TransitionOldType final int transit = getTransitCompatType( - mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps, - mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers, + mDisplayContent.mAppTransition, tmpOpenApps, + tmpCloseApps, mDisplayContent.mChangingContainers, mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(), mDisplayContent.mSkipAppTransitionAnimation); mDisplayContent.mSkipAppTransitionAnimation = false; @@ -225,22 +236,21 @@ public class AppTransitionController { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "handleAppTransitionReady: displayId=%d appTransition={%s}" + " openingApps=[%s] closingApps=[%s] transit=%s", - mDisplayContent.mDisplayId, appTransition.toString(), mDisplayContent.mOpeningApps, - mDisplayContent.mClosingApps, AppTransition.appTransitionOldToString(transit)); + mDisplayContent.mDisplayId, appTransition.toString(), tmpOpenApps, + tmpCloseApps, AppTransition.appTransitionOldToString(transit)); // Find the layout params of the top-most application window in the tokens, which is // what will control the animation theme. If all closing windows are obscured, then there is // no need to do an animation. This is the case, for example, when this transition is being // done behind a dream window. - final ArraySet activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps, - mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers); + final ArraySet activityTypes = collectActivityTypes(tmpOpenApps, + tmpCloseApps, mDisplayContent.mChangingContainers); final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes, - mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, - mDisplayContent.mChangingContainers); + tmpOpenApps, tmpCloseApps, mDisplayContent.mChangingContainers); final ActivityRecord topOpeningApp = - getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */); + getTopApp(tmpOpenApps, false /* ignoreHidden */); final ActivityRecord topClosingApp = - getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */); + getTopApp(tmpCloseApps, false /* ignoreHidden */); final ActivityRecord topChangingApp = getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */); final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity); @@ -258,8 +268,7 @@ public class AppTransitionController { final int layoutRedo; mService.mSurfaceAnimationRunner.deferStartingAnimations(); try { - applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit, - animLp, voiceInteraction); + applyAnimations(tmpOpenApps, tmpCloseApps, transit, animLp, voiceInteraction); handleClosingApps(); handleOpeningApps(); handleChangingApps(transit); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 6ff2bb30cc0c7..9398bbec4a63b 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -19,6 +19,8 @@ package com.android.server.wm; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_TO_BACK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW; @@ -32,6 +34,7 @@ import android.os.IBinder; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.SystemProperties; +import android.util.ArraySet; import android.util.Slog; import android.view.IWindowFocusObserver; import android.view.RemoteAnimationTarget; @@ -41,7 +44,6 @@ import android.window.BackNavigationInfo; import android.window.IBackAnimationFinishedCallback; import android.window.OnBackInvokedCallbackInfo; import android.window.TaskSnapshot; -import android.window.WindowContainerToken; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; @@ -60,9 +62,9 @@ class BackNavigationController { private boolean mShowWallpaper; private Runnable mPendingAnimation; - // TODO (b/241808055) Find a appropriate time to remove during refactor - // Execute back animation with legacy transition system. Temporary flag for easier debugging. - static final boolean ENABLE_SHELL_TRANSITIONS = WindowManagerService.sEnableShellTransitions; + private final AnimationTargets mAnimationTargets = new AnimationTargets(); + private final ArrayList mTmpOpenApps = new ArrayList<>(); + private final ArrayList mTmpCloseApps = new ArrayList<>(); /** * true if the back predictability feature is enabled @@ -284,7 +286,6 @@ class BackNavigationController { } if (prepareAnimation) { - infoBuilder.setDepartingWCT(toWindowContainerToken(currentTask)); prepareAnimationIfNeeded(currentTask, prevTask, prevActivity, removedWindowContainer, backType, adapter); } @@ -303,11 +304,233 @@ class BackNavigationController { return infoBuilder.build(); } - private static WindowContainerToken toWindowContainerToken(WindowContainer windowContainer) { - if (windowContainer == null || windowContainer.mRemoteToken == null) { - return null; + boolean isWaitBackTransition() { + return mAnimationTargets.mComposed && mAnimationTargets.mWaitTransition; + } + + // For legacy transition. + /** + * Once we find the transition targets match back animation targets, remove the target from + * list, so that transition won't count them in since the close animation was finished. + * + * @return {@code true} if the participants of this transition was animated by back gesture + * animations, and shouldn't join next transition. + */ + boolean removeIfContainsBackAnimationTargets(ArraySet openApps, + ArraySet closeApps) { + if (!isWaitBackTransition()) { + return false; + } + mTmpCloseApps.addAll(closeApps); + boolean result = false; + // Note: TmpOpenApps is empty. Unlike shell transition, the open apps will be removed from + // mOpeningApps if there is no visibility change. + if (mAnimationTargets.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) { + // remove close target from close list, open target from open list; + // but the open target can be in close list. + for (int i = openApps.size() - 1; i >= 0; --i) { + final ActivityRecord ar = openApps.valueAt(i); + if (mAnimationTargets.isTarget(ar, true /* open */)) { + openApps.removeAt(i); + } + } + for (int i = closeApps.size() - 1; i >= 0; --i) { + final ActivityRecord ar = closeApps.valueAt(i); + if (mAnimationTargets.isTarget(ar, false /* open */)) { + closeApps.removeAt(i); + } + } + result = true; + } + mTmpCloseApps.clear(); + return result; + } + + // For shell transition + /** + * Check whether the transition targets was animated by back gesture animation. + * Because the opening target could request to do other stuff at onResume, so it could become + * close target for a transition. So the condition here is + * The closing target should only exist in close list, but the opening target can be either in + * open or close list. + * @return {@code true} if the participants of this transition was animated by back gesture + * animations, and shouldn't join next transition. + */ + boolean containsBackAnimationTargets(Transition transition) { + if (!mAnimationTargets.mComposed + || (transition.mType != TRANSIT_CLOSE && transition.mType != TRANSIT_TO_BACK)) { + return false; + } + final ArraySet targets = transition.mParticipants; + for (int i = targets.size() - 1; i >= 0; --i) { + final WindowContainer wc = targets.valueAt(i); + if (wc.asActivityRecord() == null && wc.asTask() == null) { + continue; + } + // WC can be visible due to setLaunchBehind + if (wc.isVisibleRequested()) { + mTmpOpenApps.add(wc); + } else { + mTmpCloseApps.add(wc); + } + } + final boolean result = mAnimationTargets.containsBackAnimationTargets( + mTmpOpenApps, mTmpCloseApps); + mTmpOpenApps.clear(); + mTmpCloseApps.clear(); + return result; + } + + boolean isMonitorTransitionTarget(WindowContainer wc) { + if (!mAnimationTargets.mComposed || !mAnimationTargets.mWaitTransition) { + return false; + } + return mAnimationTargets.isTarget(wc, wc.isVisibleRequested() /* open */); + } + + /** + * Cleanup animation, this can either happen when transition ready or finish. + * @param cleanupTransaction The transaction which the caller want to apply the internal + * cleanup together. + */ + void clearBackAnimations(SurfaceControl.Transaction cleanupTransaction) { + mAnimationTargets.clearBackAnimateTarget(cleanupTransaction); + } + + /** + * TODO: Animation composer + * prepareAnimationIfNeeded will become too complicated in order to support + * ActivityRecord/WindowState, using a factory class to create the RemoteAnimationTargets for + * different scenario. + */ + private static class AnimationTargets { + ActivityRecord mCloseTarget; // Must be activity + WindowContainer mOpenTarget; // Can be activity or task if activity was removed + private boolean mComposed; + private boolean mWaitTransition; + private int mSwitchType = UNKNOWN; + private SurfaceControl.Transaction mFinishedTransaction; + + private static final int UNKNOWN = 0; + private static final int TASK_SWITCH = 1; + private static final int ACTIVITY_SWITCH = 2; + + void reset(@NonNull WindowContainer close, @NonNull WindowContainer open) { + clearBackAnimateTarget(null); + if (close.asActivityRecord() != null && open.asActivityRecord() != null + && (close.asActivityRecord().getTask() == open.asActivityRecord().getTask())) { + mSwitchType = ACTIVITY_SWITCH; + mCloseTarget = close.asActivityRecord(); + } else if (close.asTask() != null && open.asTask() != null + && close.asTask() != open.asTask()) { + mSwitchType = TASK_SWITCH; + mCloseTarget = close.asTask().getTopNonFinishingActivity(); + } else { + mSwitchType = UNKNOWN; + return; + } + + mOpenTarget = open; + mComposed = false; + mWaitTransition = false; + } + + void composeNewAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open) { + reset(close, open); + if (mSwitchType == UNKNOWN || mComposed || mCloseTarget == mOpenTarget + || mCloseTarget == null || mOpenTarget == null) { + return; + } + mComposed = true; + mWaitTransition = false; + } + + boolean containTarget(ArrayList wcs, boolean open) { + for (int i = wcs.size() - 1; i >= 0; --i) { + if (isTarget(wcs.get(i), open)) { + return true; + } + } + return wcs.isEmpty(); + } + + boolean isTarget(WindowContainer wc, boolean open) { + if (open) { + return wc == mOpenTarget || mOpenTarget.hasChild(wc); + } + if (mSwitchType == TASK_SWITCH) { + return wc == mCloseTarget + || (wc.asTask() != null && wc.hasChild(mCloseTarget)); + } else if (mSwitchType == ACTIVITY_SWITCH) { + return wc == mCloseTarget; + } + return false; + } + + boolean setFinishTransaction(SurfaceControl.Transaction finishTransaction) { + if (!mComposed) { + return false; + } + mFinishedTransaction = finishTransaction; + return true; + } + + void finishPresentAnimations(SurfaceControl.Transaction t) { + if (!mComposed) { + return; + } + final SurfaceControl.Transaction pt = t != null ? t + : mOpenTarget.getPendingTransaction(); + if (mFinishedTransaction != null) { + pt.merge(mFinishedTransaction); + mFinishedTransaction = null; + } + } + + void clearBackAnimateTarget(SurfaceControl.Transaction cleanupTransaction) { + finishPresentAnimations(cleanupTransaction); + mCloseTarget = null; + mOpenTarget = null; + mComposed = false; + mWaitTransition = false; + mSwitchType = UNKNOWN; + if (mFinishedTransaction != null) { + Slog.w(TAG, "Clear back animation, found un-processed finished transaction"); + if (cleanupTransaction != null) { + cleanupTransaction.merge(mFinishedTransaction); + } else { + mFinishedTransaction.apply(); + } + mFinishedTransaction = null; + } + } + + // The close target must in close list + // The open target can either in close or open list + boolean containsBackAnimationTargets(ArrayList openApps, + ArrayList closeApps) { + return containTarget(closeApps, false /* open */) + && (containTarget(openApps, true /* open */) + || containTarget(openApps, false /* open */)); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(128); + sb.append("AnimationTargets{"); + sb.append(" mOpenTarget= "); + sb.append(mOpenTarget); + sb.append(" mCloseTarget= "); + sb.append(mCloseTarget); + sb.append(" mSwitchType= "); + sb.append(mSwitchType); + sb.append(" mComposed= "); + sb.append(mComposed); + sb.append(" mWaitTransition= "); + sb.append(mWaitTransition); + sb.append('}'); + return sb.toString(); } - return windowContainer.mRemoteToken.toWindowContainerToken(); } private void prepareAnimationIfNeeded(Task currentTask, @@ -373,10 +596,6 @@ class BackNavigationController { leashes.add(screenshotSurface); } } else if (prevTask != null) { - if (!ENABLE_SHELL_TRANSITIONS) { - // Special handling for preventing next transition. - currentTask.mBackGestureStarted = true; - } prevActivity = prevTask.getTopNonFinishingActivity(); if (prevActivity != null) { // Make previous task show from behind by marking its top activity as visible @@ -417,35 +636,36 @@ class BackNavigationController { for (SurfaceControl sc: leashes) { finishedTransaction.remove(sc); } - synchronized (mWindowManagerService.mGlobalLock) { - if (ENABLE_SHELL_TRANSITIONS) { - if (!triggerBack) { - if (!needsScreenshot(backType)) { - restoreLaunchBehind(finalPrevActivity); - } + if (triggerBack) { + final SurfaceControl surfaceControl = + removedWindowContainer.getSurfaceControl(); + if (surfaceControl != null && surfaceControl.isValid()) { + // The animation is finish and start waiting for transition, + // hide the task surface before it re-parented to avoid flicker. + finishedTransaction.hide(surfaceControl); } + } else if (!needsScreenshot(backType)) { + restoreLaunchBehind(finalPrevActivity); + } + if (!mAnimationTargets.setFinishTransaction(finishedTransaction)) { + finishedTransaction.apply(); + } + if (!triggerBack) { + mAnimationTargets.clearBackAnimateTarget(null); } else { - if (triggerBack) { - final SurfaceControl surfaceControl = - removedWindowContainer.getSurfaceControl(); - if (surfaceControl != null && surfaceControl.isValid()) { - // When going back to home, hide the task surface before it - // re-parented to avoid flicker. - finishedTransaction.hide(surfaceControl); - } - } else { - currentTask.mBackGestureStarted = false; - if (!needsScreenshot(backType)) { - restoreLaunchBehind(finalPrevActivity); - } - } + mAnimationTargets.mWaitTransition = true; } } - finishedTransaction.apply(); + // TODO Add timeout monitor if transition didn't happen } }; - + if (backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) { + mAnimationTargets.composeNewAnimations(removedWindowContainer, prevActivity); + } else if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME + || backType == BackNavigationInfo.TYPE_CROSS_TASK) { + mAnimationTargets.composeNewAnimations(removedWindowContainer, prevTask); + } scheduleAnimationLocked(backType, targets, adapter, callback); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index d06f2716b84ab..6fd221fd2ae4b 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -67,7 +67,6 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; -import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; @@ -607,12 +606,6 @@ class Task extends TaskFragment { boolean mLastSurfaceShowing = true; - /** - * Tracks if a back gesture is in progress. - * Skips any system transition animations if this is set to {@code true}. - */ - boolean mBackGestureStarted = false; - private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, Intent _affinityIntent, String _affinity, String _rootAffinity, ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset, @@ -3331,14 +3324,6 @@ class Task extends TaskFragment { } }); } - } else if (mBackGestureStarted) { - // Cancel playing transitions if a back navigation animation is in progress. - // This bit is set by {@link BackNavigationController} when a back gesture is started. - // It is used as a one-off transition overwrite that is cleared when the back gesture - // is committed and triggers a transition, or when the gesture is cancelled. - mBackGestureStarted = false; - mDisplayContent.mSkipAppTransitionAnimation = true; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Skipping app transition animation. task=%s", this); } else { super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources); } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index a64bd694605c7..ef6859092689b 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -928,10 +928,16 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mFlags |= TRANSIT_FLAG_KEYGUARD_LOCKED; } + // Check whether the participants were animated from back navigation. + final boolean markBackAnimated = mController.mAtm.mBackNavigationController + .containsBackAnimationTargets(this); // Resolve the animating targets from the participants mTargets = calculateTargets(mParticipants, mChanges); final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges, transaction); + if (markBackAnimated) { + mController.mAtm.mBackNavigationController.clearBackAnimations(mStartTransaction); + } if (mOverrideOptions != null) { info.setAnimationOptions(mOverrideOptions); if (mOverrideOptions.getType() == ANIM_OPEN_CROSS_PROFILE_APPS) { @@ -1935,9 +1941,20 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe final Task task = wc.asTask(); if (task != null) { final ActivityRecord topActivity = task.getTopNonFinishingActivity(); - if (topActivity != null && topActivity.mStartingData != null - && topActivity.mStartingData.hasImeSurface()) { - flags |= FLAG_WILL_IME_SHOWN; + if (topActivity != null) { + if (topActivity.mStartingData != null + && topActivity.mStartingData.hasImeSurface()) { + flags |= FLAG_WILL_IME_SHOWN; + } + if (topActivity.mAtmService.mBackNavigationController + .isMonitorTransitionTarget(topActivity)) { + flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; + } + } else { + if (task.mAtmService.mBackNavigationController + .isMonitorTransitionTarget(task)) { + flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; + } } if (task.voiceSession != null) { flags |= FLAG_IS_VOICE_INTERACTION; @@ -1951,6 +1968,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe flags |= FLAG_IS_VOICE_INTERACTION; } flags |= record.mTransitionChangeFlags; + if (record.mAtmService.mBackNavigationController + .isMonitorTransitionTarget(record)) { + flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; + } } final TaskFragment taskFragment = wc.asTaskFragment(); if (taskFragment != null && task == null) {