From 312da0117a37b659f3c6a001c1138c187425c072 Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Fri, 11 Nov 2022 15:07:00 +0000 Subject: [PATCH] Decompose prepareAnimationIfNeeded into smaller methods. Use AnimationHandler to create animation leashes, which can support Activity and Task, also help handle release animation leashes. Also fix the opening animation leash get higher z-order than closing animation leash. Reimplement back-to-home, cross-activity, cross-task animation by the AnimationHandler, and by decompose prepareAnimationIfNeeded into a builder class, it should be easier to maintain. Preparing to adapt for windowless starting surface, which will draw either splash or snapshot surface on the animation leash. Bug: 131727607 Bug: 238474994 Test: atest BackNavigationControllerTests Test: do back gesture on both shell/legacy transiton system, verify no flicker happen either trigger back. Also verify no surface left after transition finish. Change-Id: I27f1df85fab792bbbcdcc430e087a3a38a74f1cc --- .../server/wm/BackNavigationController.java | 691 +++++++++++------- .../android/server/wm/SurfaceAnimator.java | 9 +- .../server/wm/TaskOrganizerController.java | 8 + .../wm/BackNavigationControllerTests.java | 9 +- 4 files changed, 426 insertions(+), 291 deletions(-) diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 14131e693561f..bbc3568464ecc 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -19,7 +19,6 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.INVALID_TASK_ID; 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.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_TO_BACK; @@ -28,12 +27,12 @@ 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.LAST_BACK_TYPE; import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Point; import android.graphics.Rect; -import android.hardware.HardwareBuffer; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteCallback; @@ -45,6 +44,7 @@ import android.util.proto.ProtoOutputStream; import android.view.IWindowFocusObserver; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; +import android.view.WindowInsets; import android.window.BackAnimationAdapter; import android.window.BackNavigationInfo; import android.window.IBackAnimationFinishedCallback; @@ -54,8 +54,11 @@ import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.server.LocalServices; +import com.android.server.wm.utils.InsetUtils; +import java.io.PrintWriter; import java.util.ArrayList; +import java.util.function.Consumer; /** * Controller to handle actions related to the back gesture on the server side. @@ -69,7 +72,7 @@ class BackNavigationController { private boolean mShowWallpaper; private Runnable mPendingAnimation; - private final AnimationTargets mAnimationTargets = new AnimationTargets(); + private AnimationHandler mAnimationHandler; private final ArrayList mTmpOpenApps = new ArrayList<>(); private final ArrayList mTmpCloseApps = new ArrayList<>(); @@ -231,7 +234,6 @@ class BackNavigationController { return infoBuilder.setType(backType).build(); } - mBackAnimationInProgress = true; // We don't have an application callback, let's find the destination of the back gesture // The search logic should align with ActivityClientController#finishActivity prevActivity = currentTask.topRunningActivity(currentActivity.token, INVALID_TASK_ID); @@ -312,8 +314,17 @@ class BackNavigationController { } if (prepareAnimation) { - prepareAnimationIfNeeded(currentTask, prevTask, prevActivity, - removedWindowContainer, backType, adapter); + mPendingAnimation = mAnimationHandler.scheduleAnimation(backType, adapter, + currentTask, prevTask, currentActivity, prevActivity); + prepareAnimation = mPendingAnimation != null; + mBackAnimationInProgress = prepareAnimation; + if (prepareAnimation) { + mWindowManagerService.mWindowPlacerLocked.requestTraversal(); + if (mShowWallpaper) { + currentTask.getDisplayContent().mWallpaperController + .adjustWallpaperWindows(); + } + } } infoBuilder.setPrepareRemoteAnimation(prepareAnimation); } // Release wm Lock @@ -331,7 +342,7 @@ class BackNavigationController { } boolean isWaitBackTransition() { - return mAnimationTargets.mComposed && mAnimationTargets.mWaitTransition; + return mAnimationHandler.mComposed && mAnimationHandler.mWaitTransition; } boolean isKeyguardOccluded(WindowState focusWindow) { @@ -357,18 +368,19 @@ class BackNavigationController { 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)) { + if (mAnimationHandler.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 */)) { + if (mAnimationHandler.isTarget(ar, true /* open */)) { openApps.removeAt(i); + mAnimationHandler.mOpenTransitionTargetMatch = true; } } for (int i = closeApps.size() - 1; i >= 0; --i) { final ActivityRecord ar = closeApps.valueAt(i); - if (mAnimationTargets.isTarget(ar, false /* open */)) { + if (mAnimationHandler.isTarget(ar, false /* open */)) { closeApps.removeAt(i); } } @@ -389,7 +401,7 @@ class BackNavigationController { * animations, and shouldn't join next transition. */ boolean containsBackAnimationTargets(Transition transition) { - if (!mAnimationTargets.mComposed + if (!mAnimationHandler.mComposed || (transition.mType != TRANSIT_CLOSE && transition.mType != TRANSIT_TO_BACK)) { return false; } @@ -406,18 +418,22 @@ class BackNavigationController { mTmpCloseApps.add(wc); } } - final boolean result = mAnimationTargets.containsBackAnimationTargets( + final boolean result = mAnimationHandler.containsBackAnimationTargets( mTmpOpenApps, mTmpCloseApps); + if (result) { + mAnimationHandler.mOpenTransitionTargetMatch = + mAnimationHandler.containTarget(mTmpOpenApps, true); + } mTmpOpenApps.clear(); mTmpCloseApps.clear(); return result; } boolean isMonitorTransitionTarget(WindowContainer wc) { - if (!mAnimationTargets.mComposed || !mAnimationTargets.mWaitTransition) { + if (!mAnimationHandler.mComposed || !mAnimationHandler.mWaitTransition) { return false; } - return mAnimationTargets.isTarget(wc, wc.isVisibleRequested() /* open */); + return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */); } /** @@ -426,60 +442,106 @@ class BackNavigationController { * cleanup together. */ void clearBackAnimations(SurfaceControl.Transaction cleanupTransaction) { - mAnimationTargets.clearBackAnimateTarget(cleanupTransaction); + mAnimationHandler.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. + * Create and handling animations status for an open/close animation targets. */ - private static class AnimationTargets { - ActivityRecord mCloseTarget; // Must be activity - WindowContainer mOpenTarget; // Can be activity or task if activity was removed + private static class AnimationHandler { + private final WindowManagerService mWindowManagerService; + private BackWindowAnimationAdaptor mCloseAdaptor; + private BackWindowAnimationAdaptor mOpenAdaptor; private boolean mComposed; private boolean mWaitTransition; private int mSwitchType = UNKNOWN; private SurfaceControl.Transaction mFinishedTransaction; + // 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; + AnimationHandler(WindowManagerService wms) { + mWindowManagerService = wms; + } 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 == null || open == null) { - Slog.e(TAG, "reset animation with null target close: " - + close + " open: " + open); - return; - } + private void initiate(WindowContainer close, WindowContainer open) { + WindowContainer closeTarget; if (close.asActivityRecord() != null && open.asActivityRecord() != null && (close.asActivityRecord().getTask() == open.asActivityRecord().getTask())) { mSwitchType = ACTIVITY_SWITCH; - mCloseTarget = close.asActivityRecord(); + closeTarget = close.asActivityRecord(); } else if (close.asTask() != null && open.asTask() != null && close.asTask() != open.asTask()) { mSwitchType = TASK_SWITCH; - mCloseTarget = close.asTask().getTopNonFinishingActivity(); + closeTarget = close.asTask().getTopNonFinishingActivity(); } else { mSwitchType = UNKNOWN; return; } - mOpenTarget = open; - mComposed = false; - mWaitTransition = false; + mCloseAdaptor = createAdaptor(closeTarget, false /* isOpen */); + mOpenAdaptor = createAdaptor(open, true /* isOpen */); + + if (mCloseAdaptor.mAnimationTarget == null || mOpenAdaptor.mAnimationTarget == null) { + Slog.w(TAG, "composeNewAnimations fail, skip"); + clearBackAnimateTarget(null /* cleanupTransaction */); + } } - void composeNewAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open) { - reset(close, open); - if (mSwitchType == UNKNOWN || mComposed || mCloseTarget == mOpenTarget - || mCloseTarget == null || mOpenTarget == null) { - return; + boolean composeAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open) { + clearBackAnimateTarget(null /* cleanupTransaction */); + if (close == null || open == null) { + Slog.e(TAG, "reset animation with null target close: " + + close + " open: " + open); + return false; + } + initiate(close, open); + if (mSwitchType == UNKNOWN) { + return false; } mComposed = true; mWaitTransition = false; + return true; + } + + RemoteAnimationTarget[] getAnimationTargets() { + return mComposed ? new RemoteAnimationTarget[] { + mCloseAdaptor.mAnimationTarget, mOpenAdaptor.mAnimationTarget} : null; + } + + boolean isSupportWindowlessSurface() { + return mWindowManagerService.mAtmService.mTaskOrganizerController + .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 wcs, boolean open) { @@ -493,13 +555,13 @@ class BackNavigationController { boolean isTarget(WindowContainer wc, boolean open) { if (open) { - return wc == mOpenTarget || mOpenTarget.hasChild(wc); + return wc == mOpenAdaptor.mTarget || mOpenAdaptor.mTarget.hasChild(wc); } if (mSwitchType == TASK_SWITCH) { - return wc == mCloseTarget - || (wc.asTask() != null && wc.hasChild(mCloseTarget)); + return wc == mCloseAdaptor.mTarget + || (wc.asTask() != null && wc.hasChild(mCloseAdaptor.mTarget)); } else if (mSwitchType == ACTIVITY_SWITCH) { - return wc == mCloseTarget; + return wc == mCloseAdaptor.mTarget; } return false; } @@ -517,19 +579,53 @@ class BackNavigationController { return; } final SurfaceControl.Transaction pt = t != null ? t - : mOpenTarget.getPendingTransaction(); + : mOpenAdaptor.mTarget.getPendingTransaction(); if (mFinishedTransaction != null) { pt.merge(mFinishedTransaction); mFinishedTransaction = null; } + cleanUpWindowlessSurface(); + + if (mCloseAdaptor != null) { + mCloseAdaptor.mTarget.cancelAnimation(); + mCloseAdaptor = null; + } + if (mOpenAdaptor != null) { + mOpenAdaptor.mTarget.cancelAnimation(); + mOpenAdaptor = null; + } + } + + 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 clearBackAnimateTarget(SurfaceControl.Transaction cleanupTransaction) { finishPresentAnimations(cleanupTransaction); - mCloseTarget = null; - mOpenTarget = null; mComposed = false; mWaitTransition = false; + mOpenTransitionTargetMatch = false; + mRequestedStartingSurfaceTaskId = 0; mSwitchType = UNKNOWN; if (mFinishedTransaction != null) { Slog.w(TAG, "Clear back animation, found un-processed finished transaction"); @@ -553,211 +649,284 @@ class BackNavigationController { @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 "AnimationTargets{" + + " openTarget= " + + mOpenAdaptor.mTarget + + " closeTarget= " + + mCloseAdaptor.mTarget + + " mSwitchType= " + + mSwitchType + + " mComposed= " + + mComposed + + " mWaitTransition= " + + mWaitTransition + + '}'; } - } - private void prepareAnimationIfNeeded(Task currentTask, - Task prevTask, ActivityRecord prevActivity, WindowContainer removedWindowContainer, - int backType, BackAnimationAdapter adapter) { - final ArrayList leashes = new ArrayList<>(); - final SurfaceControl.Transaction startedTransaction = currentTask.getPendingTransaction(); - final SurfaceControl.Transaction finishedTransaction = new SurfaceControl.Transaction(); - // Prepare a leash to animate for the departing window - final SurfaceControl animLeash = currentTask.makeAnimationLeash() - .setName("BackPreview Leash for " + currentTask) - .setHidden(false) - .build(); - removedWindowContainer.reparentSurfaceControl(startedTransaction, animLeash); + private static BackWindowAnimationAdaptor createAdaptor( + WindowContainer target, boolean isOpen) { + final BackWindowAnimationAdaptor adaptor = + new BackWindowAnimationAdaptor(target, isOpen); + target.startAnimation(target.getPendingTransaction(), adaptor, false /* hidden */, + ANIMATION_TYPE_PREDICT_BACK); + return adaptor; + } - final RemoteAnimationTarget topAppTarget = createRemoteAnimationTargetLocked( - currentTask, animLeash, MODE_CLOSING); + private static class BackWindowAnimationAdaptor implements AnimationAdapter { + SurfaceControl mCapturedLeash; + private final Rect mBounds = new Rect(); + private final WindowContainer mTarget; + private final boolean mIsOpen; + private RemoteAnimationTarget mAnimationTarget; - // reset leash after animation finished. - leashes.add(animLeash); - removedWindowContainer.reparentSurfaceControl(finishedTransaction, - removedWindowContainer.getParentSurfaceControl()); + BackWindowAnimationAdaptor(WindowContainer closeTarget, boolean isOpen) { + mBounds.set(closeTarget.getBounds()); + mTarget = closeTarget; + mIsOpen = isOpen; + } + @Override + public boolean getShowWallpaper() { + return false; + } - // Prepare a leash to animate for the entering window. - RemoteAnimationTarget behindAppTarget = null; - if (needsScreenshot(backType)) { - HardwareBuffer screenshotBuffer = null; - Task backTargetTask = prevTask; - switch(backType) { - case BackNavigationInfo.TYPE_CROSS_TASK: - int prevTaskId = prevTask != null ? prevTask.mTaskId : 0; - int prevUserId = prevTask != null ? prevTask.mUserId : 0; - screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId); - break; + @Override + public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, + int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) { + mCapturedLeash = animationLeash; + createRemoteAnimationTarget(mIsOpen); + } + + @Override + public void onAnimationCancelled(SurfaceControl animationLeash) { + if (mCapturedLeash == animationLeash) { + mCapturedLeash = null; + } + } + + @Override + public long getDurationHint() { + return 0; + } + + @Override + public long getStatusBarTransitionsStartTime() { + return 0; + } + + @Override + public void dump(PrintWriter pw, String prefix) { + pw.print(prefix + "BackWindowAnimationAdaptor mCapturedLeash="); + pw.print(mCapturedLeash); + pw.println(); + } + + @Override + public void dumpDebug(ProtoOutputStream proto) { + + } + + RemoteAnimationTarget createRemoteAnimationTarget(boolean isOpen) { + if (mAnimationTarget != null) { + return mAnimationTarget; + } + Task t = mTarget.asTask(); + final ActivityRecord r = t != null ? t.getTopNonFinishingActivity() + : mTarget.asActivityRecord(); + if (t == null && r != null) { + t = r.getTask(); + } + if (t == null || r == null) { + Slog.e(TAG, "createRemoteAnimationTarget fail " + mTarget); + return null; + } + final WindowState mainWindow = r.findMainWindow(); + Rect insets; + if (mainWindow != null) { + insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets( + mBounds, WindowInsets.Type.systemBars(), + false /* ignoreVisibility */).toRect(); + InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets()); + } else { + insets = new Rect(); + } + final int mode = isOpen ? MODE_OPENING : MODE_CLOSING; + mAnimationTarget = new RemoteAnimationTarget(t.mTaskId, mode, mCapturedLeash, + !r.fillsParent(), new Rect(), + insets, r.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top), + mBounds, mBounds, t.getWindowConfiguration(), + true /* isNotInRecents */, null, null, t.getTaskInfo(), + r.checkEnterPictureInPictureAppOpsState()); + return mAnimationTarget; + } + } + + Runnable scheduleAnimation(int backType, BackAnimationAdapter adapter, + Task currentTask, Task previousTask, ActivityRecord currentActivity, + ActivityRecord previousActivity) { + switch (backType) { + case BackNavigationInfo.TYPE_RETURN_TO_HOME: + return new ScheduleAnimationBuilder(backType, adapter) + .setIsLaunchBehind(true) + .setComposeTarget(currentTask, previousTask) + .build(); case BackNavigationInfo.TYPE_CROSS_ACTIVITY: - if (prevActivity != null && prevActivity.mActivityComponent != null) { - screenshotBuffer = getActivitySnapshot(currentTask, prevActivity); + return new ScheduleAnimationBuilder(backType, adapter) + .setComposeTarget(currentActivity, previousActivity) + .setOpeningSnapshot(getActivitySnapshot(previousActivity)).build(); + case BackNavigationInfo.TYPE_CROSS_TASK: + return new ScheduleAnimationBuilder(backType, adapter) + .setComposeTarget(currentTask, previousTask) + .setOpeningSnapshot(getTaskSnapshot(previousTask)).build(); + } + return null; + } + + private class ScheduleAnimationBuilder { + final int mType; + final BackAnimationAdapter mBackAnimationAdapter; + WindowContainer mCloseTarget; + WindowContainer mOpenTarget; + TaskSnapshot mOpenSnapshot; + boolean mIsLaunchBehind; + + ScheduleAnimationBuilder(int type, BackAnimationAdapter backAnimationAdapter) { + mType = type; + mBackAnimationAdapter = backAnimationAdapter; + } + + ScheduleAnimationBuilder setComposeTarget(WindowContainer close, WindowContainer open) { + mCloseTarget = close; + mOpenTarget = open; + return this; + } + + ScheduleAnimationBuilder setOpeningSnapshot(TaskSnapshot snapshot) { + mOpenSnapshot = snapshot; + return this; + } + + ScheduleAnimationBuilder setIsLaunchBehind(boolean launchBehind) { + mIsLaunchBehind = launchBehind; + return this; + } + + Runnable build() { + if (mOpenTarget == null || mCloseTarget == null) { + return null; + } + final boolean shouldLaunchBehind = mIsLaunchBehind || !isSupportWindowlessSurface(); + final ActivityRecord launchBehindActivity = !shouldLaunchBehind ? null + : mOpenTarget.asTask() != null + ? mOpenTarget.asTask().getTopNonFinishingActivity() + : mOpenTarget.asActivityRecord() != null + ? mOpenTarget.asActivityRecord() : null; + if (shouldLaunchBehind && launchBehindActivity == null) { + Slog.e(TAG, "No opening activity"); + return null; + } + + if (!composeAnimations(mCloseTarget, mOpenTarget)) { + return null; + } + if (launchBehindActivity != null) { + setLaunchBehind(launchBehindActivity); + } else { + createStartingSurface(mOpenSnapshot); + } + + final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback( + launchBehindActivity != null ? triggerBack -> { + if (!triggerBack) { + restoreLaunchBehind(launchBehindActivity); + } + } : null, + mCloseTarget); + final RemoteAnimationTarget[] targets = getAnimationTargets(); + + return () -> { + try { + mBackAnimationAdapter.getRunner().onAnimationStart(mType, + targets, null, null, callback); + } catch (RemoteException e) { + e.printStackTrace(); } - backTargetTask = currentTask; - break; + }; } - // Find a screenshot of the previous activity if we actually have an animation - SurfaceControl animationLeashParent = removedWindowContainer.getAnimationLeashParent(); - if (screenshotBuffer != null) { - final SurfaceControl screenshotSurface = new SurfaceControl.Builder() - .setName("BackPreview Screenshot for " + prevActivity) - .setHidden(false) - .setParent(animationLeashParent) - .setBLASTLayer() - .build(); - startedTransaction.setBuffer(screenshotSurface, screenshotBuffer); - - // The Animation leash needs to be above the screenshot surface, but the animation - // leash needs to be added before to be in the synchronized block. - startedTransaction.setLayer(topAppTarget.leash, 1); - - behindAppTarget = - createRemoteAnimationTargetLocked( - backTargetTask, screenshotSurface, MODE_OPENING); - - // reset leash after animation finished. - leashes.add(screenshotSurface); - } - } else if (prevTask != null && prevActivity != null) { - // Make previous task show from behind by marking its top activity as visible - // and launch-behind to bump its visibility for the duration of the back gesture. - setLaunchBehind(prevActivity); - - final SurfaceControl leash = prevActivity.makeAnimationLeash() - .setName("BackPreview Leash for " + prevActivity) - .setHidden(false) - .build(); - prevActivity.reparentSurfaceControl(startedTransaction, leash); - behindAppTarget = createRemoteAnimationTargetLocked( - prevTask, leash, MODE_OPENING); - - // reset leash after animation finished. - leashes.add(leash); - prevActivity.reparentSurfaceControl(finishedTransaction, - prevActivity.getParentSurfaceControl()); - } - - if (mShowWallpaper) { - currentTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows(); - // TODO(b/241808055): If the current animation need to show wallpaper and animate the - // wallpaper, start the wallpaper animation to collect wallpaper target and deliver it - // to the back animation controller. - } - - final RemoteAnimationTarget[] targets = (behindAppTarget == null) - ? new RemoteAnimationTarget[] {topAppTarget} - : new RemoteAnimationTarget[] {topAppTarget, behindAppTarget}; - - final ActivityRecord finalPrevActivity = prevActivity; - final IBackAnimationFinishedCallback callback = - new IBackAnimationFinishedCallback.Stub() { + private IBackAnimationFinishedCallback makeAnimationFinishedCallback( + Consumer b, WindowContainer closeTarget) { + return new IBackAnimationFinishedCallback.Stub() { @Override public void onAnimationFinished(boolean triggerBack) { - for (SurfaceControl sc: leashes) { - finishedTransaction.remove(sc); - } + final SurfaceControl.Transaction finishedTransaction = + new SurfaceControl.Transaction(); synchronized (mWindowManagerService.mGlobalLock) { + if (b != null) { + b.accept(triggerBack); + } if (triggerBack) { final SurfaceControl surfaceControl = - removedWindowContainer.getSurfaceControl(); + closeTarget.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. + // Hide the close target surface when transition start. finishedTransaction.hide(surfaceControl); } - } else if (!needsScreenshot(backType)) { - restoreLaunchBehind(finalPrevActivity); } - if (!mAnimationTargets.setFinishTransaction(finishedTransaction)) { + if (!setFinishTransaction(finishedTransaction)) { finishedTransaction.apply(); } if (!triggerBack) { - mAnimationTargets.clearBackAnimateTarget(null); + clearBackAnimateTarget( + null /* cleanupTransaction */); } else { - mAnimationTargets.mWaitTransition = true; + mWaitTransition = true; } } // 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); - } - - @NonNull - private static RemoteAnimationTarget createRemoteAnimationTargetLocked( - Task task, SurfaceControl animLeash, int mode) { - ActivityRecord topApp = task.getTopRealVisibleActivity(); - if (topApp == null) { - topApp = task.getTopNonFinishingActivity(); - } - - final WindowState mainWindow = topApp != null - ? topApp.findMainWindow() - : null; - int windowType = INVALID_WINDOW_TYPE; - if (mainWindow != null) { - windowType = mainWindow.getWindowType(); - } - - Rect bounds = new Rect(task.getBounds()); - Rect localBounds = new Rect(bounds); - Point tmpPos = new Point(); - task.getRelativePosition(tmpPos); - localBounds.offsetTo(tmpPos.x, tmpPos.y); - - return new RemoteAnimationTarget( - task.mTaskId, - mode, - animLeash, - false /* isTransluscent */, - new Rect() /* clipRect */, - new Rect() /* contentInsets */, - task.getPrefixOrderIndex(), - tmpPos /* position */, - localBounds /* localBounds */, - bounds /* screenSpaceBounds */, - task.getWindowConfiguration(), - true /* isNotInRecent */, - null, - null, - task.getTaskInfo(), - false, - windowType); - } - - @VisibleForTesting - void scheduleAnimationLocked(@BackNavigationInfo.BackTargetType int type, - RemoteAnimationTarget[] targets, BackAnimationAdapter backAnimationAdapter, - IBackAnimationFinishedCallback callback) { - mPendingAnimation = () -> { - try { - backAnimationAdapter.getRunner().onAnimationStart(type, - targets, null, null, callback); - } catch (RemoteException e) { - e.printStackTrace(); } - }; - mWindowManagerService.mWindowPlacerLocked.requestTraversal(); + + private void setLaunchBehind(ActivityRecord activity) { + if (activity == null) { + return; + } + if (!activity.isVisibleRequested()) { + activity.setVisibility(true); + } + activity.mLaunchTaskBehind = true; + + // Handle fixed rotation launching app. + final DisplayContent dc = activity.mDisplayContent; + dc.rotateInDifferentOrientationIfNeeded(activity); + if (activity.hasFixedRotationTransform()) { + // Set the record so we can recognize it to continue to update display + // orientation if the previous activity becomes the top later. + dc.setFixedRotationLaunchingApp(activity, + activity.getWindowConfiguration().getRotation()); + } + + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity); + activity.mTaskSupervisor.mStoppingActivities.remove(activity); + activity.getDisplayContent().ensureActivitiesVisible(null /* starting */, + 0 /* configChanges */, false /* preserveWindows */, true); + } + private void restoreLaunchBehind(ActivityRecord activity) { + if (activity == null) { + return; + } + + activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); + + // Restore the launch-behind state. + activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token); + activity.mLaunchTaskBehind = false; + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to false. Activity=%s", + activity); + } + } } void checkAnimationReady(WallpaperController wallpaperController) { @@ -794,80 +963,36 @@ class BackNavigationController { mShowWallpaper = false; } - private HardwareBuffer getActivitySnapshot(@NonNull Task task, ActivityRecord r) { - return task.getSnapshotForActivityRecord(r); - } - - private HardwareBuffer getTaskSnapshot(int taskId, int userId) { - if (mWindowManagerService.mTaskSnapshotController == null) { + private static TaskSnapshot getActivitySnapshot(@NonNull ActivityRecord r) { + if (!isScreenshotEnabled()) { return null; } - TaskSnapshot snapshot = mWindowManagerService.mTaskSnapshotController.getSnapshot(taskId, - userId, true /* restoreFromDisk */, false /* isLowResolution */); - return snapshot != null ? snapshot.getHardwareBuffer() : null; + // Check if we have a screenshot of the previous activity, indexed by its + // component name. + // TODO return TaskSnapshot when feature complete. +// final HardwareBuffer hw = r.getTask().getSnapshotForActivityRecord(r); + return null; } - private boolean needsScreenshot(int backType) { + private static TaskSnapshot getTaskSnapshot(Task task) { if (!isScreenshotEnabled()) { - return false; + return null; } - switch (backType) { - case BackNavigationInfo.TYPE_RETURN_TO_HOME: - case BackNavigationInfo.TYPE_DIALOG_CLOSE: - return false; - } - return true; + // Don't read from disk!! + return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot( + task.mTaskId, task.mUserId, false /* restoreFromDisk */, + false /* isLowResolution */); } void setWindowManager(WindowManagerService wm) { mWindowManagerService = wm; - } - - private void setLaunchBehind(ActivityRecord activity) { - if (activity == null) { - return; - } - if (!activity.isVisibleRequested()) { - activity.setVisibility(true); - } - activity.mLaunchTaskBehind = true; - - // Handle fixed rotation launching app. - final DisplayContent dc = activity.mDisplayContent; - dc.rotateInDifferentOrientationIfNeeded(activity); - if (activity.hasFixedRotationTransform()) { - // Set the record so we can recognize it to continue to update display orientation - // if the previous activity becomes the top later. - dc.setFixedRotationLaunchingApp(activity, - activity.getWindowConfiguration().getRotation()); - } - - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, - "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity); - activity.mTaskSupervisor.mStoppingActivities.remove(activity); - activity.getDisplayContent().ensureActivitiesVisible(null /* starting */, - 0 /* configChanges */, false /* preserveWindows */, true); - } - - private void restoreLaunchBehind(ActivityRecord activity) { - if (activity == null) { - return; - } - - activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); - - // Restore the launch-behind state. - activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token); - activity.mLaunchTaskBehind = false; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, - "Setting Activity.mLauncherTaskBehind to false. Activity=%s", - activity); + mAnimationHandler = new AnimationHandler(wm); } boolean isWallpaperVisible(WindowState w) { - return mAnimationTargets.mComposed && mShowWallpaper + return mAnimationHandler.mComposed && mShowWallpaper && w.mAttrs.type == TYPE_BASE_APPLICATION && w.mActivityRecord != null - && mAnimationTargets.isTarget(w.mActivityRecord, true /* open */); + && mAnimationHandler.isTarget(w.mActivityRecord, true /* open */); } // Called from WindowManagerService to write to a protocol buffer output stream. diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index ca86db9e4f7c6..408ea6eb8c1aa 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -564,6 +564,11 @@ class SurfaceAnimator { */ public static final int ANIMATION_TYPE_STARTING_REVEAL = 1 << 7; + /** + * Animation when a back gesture animation is applied to a window container. + * @hide + */ + public static final int ANIMATION_TYPE_PREDICT_BACK = 1 << 8; /** * Bitmask to include all animation types. This is NOT an {@link AnimationType} * @hide @@ -583,7 +588,8 @@ class SurfaceAnimator { ANIMATION_TYPE_WINDOW_ANIMATION, ANIMATION_TYPE_INSETS_CONTROL, ANIMATION_TYPE_TOKEN_TRANSFORM, - ANIMATION_TYPE_STARTING_REVEAL + ANIMATION_TYPE_STARTING_REVEAL, + ANIMATION_TYPE_PREDICT_BACK }) @Retention(RetentionPolicy.SOURCE) @interface AnimationType {} @@ -602,6 +608,7 @@ class SurfaceAnimator { case ANIMATION_TYPE_INSETS_CONTROL: return "insets_animation"; case ANIMATION_TYPE_TOKEN_TRANSFORM: return "token_transform"; case ANIMATION_TYPE_STARTING_REVEAL: return "starting_reveal"; + case ANIMATION_TYPE_PREDICT_BACK: return "predict_back"; default: return "unknown type:" + type; } } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 8570db275a080..3a30e4b0cf1f4 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -722,6 +722,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return true; } + boolean isSupportWindowlessStartingSurface() { + // Enable after ag/20426257 + final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast(); + if (lastOrganizer == null) { + return false; + } + return false; + } /** * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has * removed the splash screen view. 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 9a786d447507f..d08f397a91c17 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -32,7 +32,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; @@ -92,9 +91,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); // verify if back animation would start. - verify(mBackNavigationController).scheduleAnimationLocked( - eq(BackNavigationInfo.TYPE_RETURN_TO_HOME), any(), eq(mBackAnimationAdapter), - any()); + assertTrue("Animation scheduled", backNavigationInfo.isPrepareRemoteAnimation()); } @Test @@ -111,9 +108,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_TASK)); // verify if back animation would start. - verify(mBackNavigationController).scheduleAnimationLocked( - eq(BackNavigationInfo.TYPE_CROSS_TASK), any(), eq(mBackAnimationAdapter), - any()); + assertTrue("Animation scheduled", backNavigationInfo.isPrepareRemoteAnimation()); // reset drawning status topTask.forAllWindows(w -> {