diff options
| author | 2023-02-14 18:31:52 +0800 | |
|---|---|---|
| committer | 2023-07-27 06:24:19 +0000 | |
| commit | d8dfb19c46cb04474b5f2a66cb34a50b3dbdfc47 (patch) | |
| tree | 70da6d46cb0ad583256af5819ac673c4fe2d4510 | |
| parent | 3490e6fb8fe5282134aa542f2cde6d66aafbbea4 (diff) | |
Create windowless starting surface on opening target.
If config_predictShowStartingSurface has enabled, create a windowless
starting surface, and attach it on the opening animation target. And
remove the starting surface when transition is about to happen.
Need to hide the close target when transition start, because unlike
launchTaskBehind, when using windowless starting surface to the opening
target, the opening target will also be collect in next transition, so
both open and close will be reparent to transition root while apply the
start transaction, which may cause flicker.
Bug: 257857570
Bug: 268563842
Test: manual, trigger a cross-task/activity animation, and verify
snapshot or splash starting surface can draw on the open target.
Test: atest BackGestureInvokedTest BackNavigationControllerTests
Change-Id: I5c703909faa4538f9ff89e4fd5879e84684611df
6 files changed, 154 insertions, 87 deletions
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 325ebbe885b4..8e619a8bda48 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -609,4 +609,6 @@ message BackNavigationProto { optional bool animation_in_progress = 1; optional int32 last_back_type = 2; optional bool show_wallpaper = 3; + optional string main_open_activity = 4; + optional bool animation_running = 5; }
\ No newline at end of file diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e93f35802953..0c6bad886ebd 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -226,6 +226,7 @@ import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW; import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE; @@ -7632,7 +7633,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override void prepareSurfaces() { final boolean show = isVisible() || isAnimating(PARENTS, - ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS); + ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS + | ANIMATION_TYPE_PREDICT_BACK); if (mSurfaceControl != null) { if (show && !mLastSurfaceShowing) { diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 0c196d7e99e9..976641b52a16 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -26,7 +26,9 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW; import static com.android.server.wm.BackNavigationProto.ANIMATION_IN_PROGRESS; +import static com.android.server.wm.BackNavigationProto.ANIMATION_RUNNING; import static com.android.server.wm.BackNavigationProto.LAST_BACK_TYPE; +import static com.android.server.wm.BackNavigationProto.MAIN_OPEN_ACTIVITY; import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK; @@ -50,6 +52,7 @@ import android.view.WindowInsets; import android.window.BackAnimationAdapter; import android.window.BackNavigationInfo; import android.window.IBackAnimationFinishedCallback; +import android.window.IWindowlessStartingSurfaceCallback; import android.window.OnBackInvokedCallbackInfo; import android.window.TaskSnapshot; @@ -73,6 +76,8 @@ class BackNavigationController { private @BackNavigationInfo.BackTargetType int mLastBackType; private boolean mShowWallpaper; private Runnable mPendingAnimation; + + private boolean mBackAnimationRunning; private final NavigationMonitor mNavigationMonitor = new NavigationMonitor(); private AnimationHandler mAnimationHandler; @@ -474,7 +479,7 @@ class BackNavigationController { final ActivityRecord ar = openApps.valueAt(i); if (mAnimationHandler.isTarget(ar, true /* open */)) { openApps.removeAt(i); - mAnimationHandler.mOpenTransitionTargetMatch = true; + mAnimationHandler.markStartingSurfaceMatch(); } } for (int i = closeApps.size() - 1; i >= 0; --i) { @@ -583,8 +588,9 @@ class BackNavigationController { * The closing target should only exist in close list, but the opening target can be either in * open or close list. */ - void onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets) { - if (!isMonitoringTransition()) { + void onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets, + SurfaceControl.Transaction startTransaction) { + if (!isMonitoringTransition() || targets.isEmpty()) { return; } for (int i = targets.size() - 1; i >= 0; --i) { @@ -613,6 +619,17 @@ class BackNavigationController { Slog.e(TAG, "Gesture animation is applied on another transition?"); } mWaitTransitionFinish = transition; + // Flag target matches to defer remove the splash screen. + for (int i = mTmpOpenApps.size() - 1; i >= 0; --i) { + final WindowContainer wc = mTmpOpenApps.get(i); + if (mAnimationHandler.isTarget(wc, true /* open */)) { + mAnimationHandler.markStartingSurfaceMatch(); + break; + } + } + // Because the target will reparent to transition root, so it cannot be controlled by + // animation leash. Hide the close target when transition starts. + startTransaction.hide(mAnimationHandler.mCloseAdaptor.mTarget.getSurfaceControl()); } mTmpOpenApps.clear(); mTmpCloseApps.clear(); @@ -633,6 +650,7 @@ class BackNavigationController { mAnimationHandler.clearBackAnimateTarget(); mNavigationMonitor.stopMonitorTransition(); mWaitTransitionFinish = null; + mBackAnimationRunning = false; } /** @@ -717,11 +735,7 @@ class BackNavigationController { // This will be set before transition happen, to know whether the real opening target // exactly match animating target. When target match, reparent the starting surface to // the opening target like starting window do. - private boolean mOpenTransitionTargetMatch; - // The starting surface task Id. Used to clear the starting surface if the animation has - // request one during animating. - private int mRequestedStartingSurfaceTaskId; - private SurfaceControl mStartingSurface; + private boolean mStartingSurfaceTargetMatch; private ActivityRecord mOpenActivity; AnimationHandler(WindowManagerService wms) { @@ -765,8 +779,8 @@ class BackNavigationController { return; } - mCloseAdaptor = createAdaptor(closeTarget, false /* isOpen */); - mOpenAdaptor = createAdaptor(open, true /* isOpen */); + mCloseAdaptor = createAdaptor(closeTarget, false, mSwitchType); + mOpenAdaptor = createAdaptor(open, true, mSwitchType); mOpenActivity = openActivity; if (mCloseAdaptor.mAnimationTarget == null || mOpenAdaptor.mAnimationTarget == null) { Slog.w(TAG, "composeNewAnimations fail, skip"); @@ -774,8 +788,8 @@ class BackNavigationController { } } - boolean composeAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open, - ActivityRecord openActivity) { + private boolean composeAnimations(@NonNull WindowContainer close, + @NonNull WindowContainer open, ActivityRecord openActivity) { if (mComposed || mWaitTransition) { Slog.e(TAG, "Previous animation is running " + this); return false; @@ -805,28 +819,6 @@ class BackNavigationController { .isSupportWindowlessStartingSurface(); } - void createStartingSurface(TaskSnapshot snapshot) { - if (!mComposed) { - return; - } - - final ActivityRecord topActivity = getTopOpenActivity(); - if (topActivity == null) { - Slog.e(TAG, "createStartingSurface fail, no open activity: " + this); - return; - } - // TODO (b/257857570) draw snapshot by starting surface. - } - - private ActivityRecord getTopOpenActivity() { - if (mSwitchType == ACTIVITY_SWITCH) { - return mOpenAdaptor.mTarget.asActivityRecord(); - } else if (mSwitchType == TASK_SWITCH) { - return mOpenAdaptor.mTarget.asTask().getTopNonFinishingActivity(); - } - return null; - } - boolean containTarget(ArrayList<WindowContainer> wcs, boolean open) { for (int i = wcs.size() - 1; i >= 0; --i) { if (isTarget(wcs.get(i), open)) { @@ -860,13 +852,13 @@ class BackNavigationController { if (!mComposed) { return; } - cleanUpWindowlessSurface(); if (mCloseAdaptor != null) { mCloseAdaptor.mTarget.cancelAnimation(); mCloseAdaptor = null; } if (mOpenAdaptor != null) { + mOpenAdaptor.cleanUpWindowlessSurface(mStartingSurfaceTargetMatch); mOpenAdaptor.mTarget.cancelAnimation(); mOpenAdaptor = null; } @@ -875,36 +867,16 @@ class BackNavigationController { } } - private void cleanUpWindowlessSurface() { - final ActivityRecord ar = getTopOpenActivity(); - if (ar == null) { - Slog.w(TAG, "finishPresentAnimations without top activity: " + this); - } - final SurfaceControl.Transaction pendingT = ar != null ? ar.getPendingTransaction() - : mOpenAdaptor.mTarget.getPendingTransaction(); - // ensure open target is visible before cancel animation. - mOpenTransitionTargetMatch &= ar != null; - if (mOpenTransitionTargetMatch) { - pendingT.show(ar.getSurfaceControl()); - } - if (mRequestedStartingSurfaceTaskId != 0) { - // If open target match, reparent to open activity - if (mStartingSurface != null && mOpenTransitionTargetMatch) { - pendingT.reparent(mStartingSurface, ar.getSurfaceControl()); - } - // remove starting surface. - mStartingSurface = null; - // TODO (b/257857570) draw snapshot by starting surface. - mRequestedStartingSurfaceTaskId = 0; - } + void markStartingSurfaceMatch() { + mStartingSurfaceTargetMatch = true; + mOpenAdaptor.reparentWindowlessSurfaceToTarget(); } void clearBackAnimateTarget() { finishPresentAnimations(); mComposed = false; mWaitTransition = false; - mOpenTransitionTargetMatch = false; - mRequestedStartingSurfaceTaskId = 0; + mStartingSurfaceTargetMatch = false; mSwitchType = UNKNOWN; mOpenActivity = null; } @@ -935,9 +907,9 @@ class BackNavigationController { } private static BackWindowAnimationAdaptor createAdaptor( - WindowContainer target, boolean isOpen) { + WindowContainer target, boolean isOpen, int switchType) { final BackWindowAnimationAdaptor adaptor = - new BackWindowAnimationAdaptor(target, isOpen); + new BackWindowAnimationAdaptor(target, isOpen, switchType); final SurfaceControl.Transaction pt = target.getPendingTransaction(); target.startAnimation(pt, adaptor, false /* hidden */, ANIMATION_TYPE_PREDICT_BACK); // Workaround to show TaskFragment which can be hide in Transitions and won't show @@ -957,11 +929,19 @@ class BackNavigationController { private final WindowContainer mTarget; private final boolean mIsOpen; private RemoteAnimationTarget mAnimationTarget; + private final int mSwitchType; - BackWindowAnimationAdaptor(WindowContainer closeTarget, boolean isOpen) { - mBounds.set(closeTarget.getBounds()); - mTarget = closeTarget; + // The starting surface task Id. Used to clear the starting surface if the animation has + // requested one during animating. + private int mRequestedStartingSurfaceId = INVALID_TASK_ID; + private SurfaceControl mStartingSurface; + + BackWindowAnimationAdaptor(WindowContainer target, boolean isOpen, + int switchType) { + mBounds.set(target.getBounds()); + mTarget = target; mIsOpen = isOpen; + mSwitchType = switchType; } @Override public boolean getShowWallpaper() { @@ -979,6 +959,8 @@ class BackNavigationController { public void onAnimationCancelled(SurfaceControl animationLeash) { if (mCapturedLeash == animationLeash) { mCapturedLeash = null; + mRequestedStartingSurfaceId = INVALID_TASK_ID; + mStartingSurface = null; } } @@ -1009,8 +991,15 @@ class BackNavigationController { return mAnimationTarget; } Task t = mTarget.asTask(); - final ActivityRecord r = t != null ? t.getTopNonFinishingActivity() - : mTarget.asActivityRecord(); + ActivityRecord r = null; + if (t == null && mTarget.asTaskFragment() != null) { + t = mTarget.asTaskFragment().getTask(); + r = mTarget.asTaskFragment().getTopNonFinishingActivity(); + } + if (r == null) { + r = t != null ? t.getTopNonFinishingActivity() + : mTarget.asActivityRecord(); + } if (t == null && r != null) { t = r.getTask(); } @@ -1037,6 +1026,77 @@ class BackNavigationController { r.checkEnterPictureInPictureAppOpsState()); return mAnimationTarget; } + + void createStartingSurface() { + if (!mIsOpen) { + return; + } + final Task openTask = mSwitchType == TASK_SWITCH + ? mTarget.asTask() : mSwitchType == ACTIVITY_SWITCH + ? mTarget.asActivityRecord().getTask() : null; + if (openTask == null) { + return; + } + final ActivityRecord mainActivity = mSwitchType == ACTIVITY_SWITCH + ? mTarget.asActivityRecord() + : openTask.getTopNonFinishingActivity(); + if (mainActivity == null) { + return; + } + final TaskSnapshot snapshot = getSnapshot(mTarget); + mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController + .addWindowlessStartingSurface(openTask, mainActivity, + mAnimationTarget.leash, snapshot, + new IWindowlessStartingSurfaceCallback.Stub() { + // Once the starting surface has been created in shell, it will call + // onSurfaceAdded to pass the created surface to core, so if a + // transition is triggered by the back gesture, there doesn't need to + // create another starting surface for the opening target, just reparent + // the starting surface to the opening target. + // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded + // called, there won't be able to reparent the starting surface on + // opening target. But if that happens and transition target is matched, + // the app window should already draw. + @Override + public void onSurfaceAdded(SurfaceControl sc) { + synchronized (mTarget.mWmService.mGlobalLock) { + if (mRequestedStartingSurfaceId != INVALID_TASK_ID) { + mStartingSurface = sc; + } + } + } + }); + } + + // When back gesture has triggered and transition target matches navigation target, + // reparent the starting surface to the opening target as it's starting window. + void reparentWindowlessSurfaceToTarget() { + if (mRequestedStartingSurfaceId == INVALID_TASK_ID) { + return; + } + // If open target matches, reparent to open activity or task + if (mStartingSurface != null && mStartingSurface.isValid()) { + mTarget.getPendingTransaction() + .reparent(mStartingSurface, mTarget.getSurfaceControl()); + // remove starting surface. + mStartingSurface = null; + } + } + + /** + * Ask shell to clear the starting surface. + * @param openTransitionMatch if true, shell will play the remove starting window + * animation, otherwise remove it directly. + */ + void cleanUpWindowlessSurface(boolean openTransitionMatch) { + if (mRequestedStartingSurfaceId == INVALID_TASK_ID) { + return; + } + mTarget.mWmService.mAtmService.mTaskOrganizerController + .removeWindowlessStartingSurface(mRequestedStartingSurfaceId, + !openTransitionMatch); + mRequestedStartingSurfaceId = INVALID_TASK_ID; + } } ScheduleAnimationBuilder prepareAnimation(int backType, BackAnimationAdapter adapter, @@ -1089,15 +1149,13 @@ class BackNavigationController { /** * Apply preview strategy on the opening target - * @param open The opening target. + * @param openAnimationAdaptor The animator who can create starting surface. * @param visibleOpenActivity The visible activity in opening target. - * @return If the preview strategy is launch behind, returns the Activity that has - * launchBehind set, or null otherwise. */ - private void applyPreviewStrategy(WindowContainer open, + private void applyPreviewStrategy(BackWindowAnimationAdaptor openAnimationAdaptor, ActivityRecord visibleOpenActivity) { if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) { - createStartingSurface(getSnapshot(open)); + openAnimationAdaptor.createStartingSurface(); return; } setLaunchBehind(visibleOpenActivity); @@ -1119,7 +1177,7 @@ class BackNavigationController { if (!composeAnimations(mCloseTarget, mOpenTarget, openActivity)) { return null; } - applyPreviewStrategy(mOpenTarget, openActivity); + applyPreviewStrategy(mOpenAdaptor, openActivity); final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback(); final RemoteAnimationTarget[] targets = getAnimationTargets(); @@ -1220,6 +1278,7 @@ class BackNavigationController { if (mPendingAnimation != null) { mPendingAnimation.run(); mPendingAnimation = null; + mBackAnimationRunning = true; } } @@ -1236,9 +1295,6 @@ class BackNavigationController { } static TaskSnapshot getSnapshot(@NonNull WindowContainer w) { - if (!isScreenshotEnabled()) { - return null; - } if (w.asTask() != null) { final Task task = w.asTask(); return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot( @@ -1247,8 +1303,8 @@ class BackNavigationController { } if (w.asActivityRecord() != null) { - // TODO (b/259497289) return TaskSnapshot when feature complete. - return null; + final ActivityRecord ar = w.asActivityRecord(); + return ar.mWmService.mSnapshotController.mActivitySnapshotController.getSnapshot(ar); } return null; } @@ -1270,6 +1326,12 @@ class BackNavigationController { proto.write(ANIMATION_IN_PROGRESS, mBackAnimationInProgress); proto.write(LAST_BACK_TYPE, mLastBackType); proto.write(SHOW_WALLPAPER, mShowWallpaper); + if (mAnimationHandler.mOpenActivity != null) { + mAnimationHandler.mOpenActivity.writeNameToProto(proto, MAIN_OPEN_ACTIVITY); + } else { + proto.write(MAIN_OPEN_ACTIVITY, ""); + } + proto.write(ANIMATION_RUNNING, mBackAnimationRunning); proto.end(token); } } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index cdb4ad645dc3..b72d02789114 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -790,12 +790,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } boolean isSupportWindowlessStartingSurface() { - // Enable after ag/20426257 final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast(); - if (lastOrganizer == null) { - return false; - } - return false; + return lastOrganizer != null; } /** * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index b7c8092e3774..f33af5efb691 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1499,7 +1499,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mTargets = calculateTargets(mParticipants, mChanges); // Check whether the participants were animated from back navigation. - mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets); + mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets, + transaction); final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction); info.setDebugId(mSyncId); mController.assignTrack(this, info); diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index 6d7f2c13197c..1c86758f28ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -570,6 +570,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { final ContextWrapper contextSpy = Mockito.spy(new ContextWrapper(mWm.mContext)); final Resources resourcesSpy = Mockito.spy(contextSpy.getResources()); + spyOn(mAtm.mTaskOrganizerController); when(contextSpy.getResources()).thenReturn(resourcesSpy); MockitoSession mockitoSession = mockitoSession().mockStatic(BackNavigationController.class) @@ -597,7 +598,8 @@ public class BackNavigationControllerTests extends WindowTestsBase { mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, homeActivity); assertTrue(toHomeBuilder.mIsLaunchBehind); toHomeBuilder.build(); - verify(animationHandler, never()).createStartingSurface(any()); + verify(mAtm.mTaskOrganizerController, never()) + .addWindowlessStartingSurface(any(), any(), any(), any(), any()); animationHandler.clearBackAnimateTarget(); // Back to ACTIVITY and TASK have the same logic, just with different target. @@ -609,9 +611,11 @@ public class BackNavigationControllerTests extends WindowTestsBase { assertFalse(toActivityBuilder.mIsLaunchBehind); toActivityBuilder.build(); if (preferWindowlessSurface) { - verify(animationHandler).createStartingSurface(any()); + verify(mAtm.mTaskOrganizerController) + .addWindowlessStartingSurface(any(), any(), any(), any(), any()); } else { - verify(animationHandler, never()).createStartingSurface(any()); + verify(mAtm.mTaskOrganizerController, never()) + .addWindowlessStartingSurface(any(), any(), any(), any(), any()); } } |