diff options
5 files changed, 243 insertions, 26 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index 40065b9287a6..9016c45e8197 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -34,6 +34,8 @@ import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION; import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; +import static com.android.wm.shell.transition.Transitions.TRANSIT_END_RECENTS_TRANSITION; +import static com.android.wm.shell.transition.Transitions.TRANSIT_START_RECENTS_TRANSITION; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -67,6 +69,7 @@ import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.protolog.ProtoLog; +import com.android.wm.shell.Flags; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipUtils; @@ -216,8 +219,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, break; } } - final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct, - mixer == null ? this : mixer); + final int transitionType = Flags.enableShellTopTaskTracking() + ? TRANSIT_START_RECENTS_TRANSITION + : TRANSIT_TO_FRONT; + final IBinder transition = mTransitions.startTransition(transitionType, + wct, mixer == null ? this : mixer); if (mixer != null) { setTransitionForMixer.accept(transition); } @@ -300,7 +306,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, "RecentsTransitionHandler.mergeAnimation: no controller found"); return; } - controller.merge(info, t, finishCallback); + controller.merge(info, t, mergeTarget, finishCallback); } @Override @@ -367,6 +373,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, private boolean mPausingSeparateHome = false; private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null; private PictureInPictureSurfaceTransaction mPipTransaction = null; + // This is the transition that backs the entire recents transition, and the one that the + // pending finish transition below will be merged into private IBinder mTransition = null; private boolean mKeyguardLocked = false; private boolean mWillFinishToHome = false; @@ -386,6 +394,10 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, // next called. private Pair<int[], TaskSnapshot[]> mPendingPauseSnapshotsForCancel; + // Used to track a pending finish transition + private IBinder mPendingFinishTransition; + private IResultReceiver mPendingRunnerFinishCb; + RecentsController(IRecentsAnimationRunner listener) { mInstanceId = System.identityHashCode(this); mListener = listener; @@ -523,6 +535,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, mInfo = null; mTransition = null; mPendingPauseSnapshotsForCancel = null; + mPipTaskId = -1; + mPipTask = null; + mPipTransaction = null; + mPendingRunnerFinishCb = null; + mPendingFinishTransition = null; mControllers.remove(this); for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onAnimationStateChanged(false); @@ -734,6 +751,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, // the pausing apps. t.setLayer(target.leash, layer); } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " not handling home taskId=%d", taskInfo.taskId); // do nothing } else if (TransitionUtil.isOpeningType(change.getMode())) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, @@ -872,16 +891,35 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, } } + /** + * Note: because we use a book-end transition to finish the recents transition, we must + * either always merge the incoming transition, or always cancel the recents transition + * if we don't handle the incoming transition to ensure that the end transition is queued + * before any unhandled transitions. + */ @SuppressLint("NewApi") - void merge(TransitionInfo info, SurfaceControl.Transaction t, + void merge(TransitionInfo info, SurfaceControl.Transaction t, IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) { if (mFinishCB == null) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.merge: skip, no finish callback", mInstanceId); - // This was no-op'd (likely a repeated start) and we've already sent finish. + // This was no-op'd (likely a repeated start) and we've already completed finish. + return; + } + + if (Flags.enableShellTopTaskTracking() + && info.getType() == TRANSIT_END_RECENTS_TRANSITION + && mergeTarget == mTransition) { + // This is a pending finish, so merge the end transition to trigger completing the + // cleanup of the recents transition + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.merge: TRANSIT_END_RECENTS_TRANSITION", + mInstanceId); + finishCallback.onTransitionFinished(null /* wct */); return; } + if (info.getType() == TRANSIT_SLEEP) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.merge: transit_sleep", mInstanceId); @@ -1245,7 +1283,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, return; } - if (mFinishCB == null) { + if (mFinishCB == null + || (Flags.enableShellTopTaskTracking() && mPendingFinishTransition != null)) { Slog.e(TAG, "Duplicate call to finish"); return; } @@ -1254,19 +1293,22 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL; - if (returningToApp && allAppsAreTranslucent(mPausingTasks)) { - mHomeTransitionObserver.notifyHomeVisibilityChanged(true); - } else if (!toHome) { - // For some transitions, we may have notified home activity that it became visible. - // We need to notify the observer that we are no longer going home. - mHomeTransitionObserver.notifyHomeVisibilityChanged(false); + if (!Flags.enableShellTopTaskTracking()) { + // This is only necessary when the recents transition is finished using a finishWCT, + // otherwise a new transition will notify the relevant observers + if (returningToApp && allAppsAreTranslucent(mPausingTasks)) { + mHomeTransitionObserver.notifyHomeVisibilityChanged(true); + } else if (!toHome) { + // For some transitions, we may have notified home activity that it became + // visible. We need to notify the observer that we are no longer going home. + mHomeTransitionObserver.notifyHomeVisibilityChanged(false); + } } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.finishInner: toHome=%b userLeave=%b " - + "willFinishToHome=%b state=%d", - mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState); - final Transitions.TransitionFinishCallback finishCB = mFinishCB; - mFinishCB = null; + + "willFinishToHome=%b state=%d reason=%s", + mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState, reason); final SurfaceControl.Transaction t = mFinishTransaction; final WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -1328,6 +1370,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, for (int i = 0; i < mClosingTasks.size(); ++i) { cleanUpPausingOrClosingTask(mClosingTasks.get(i), wct, t, sendUserLeaveHint); } + if (mPipTransaction != null && sendUserLeaveHint) { SurfaceControl pipLeash = null; TransitionInfo.Change pipChange = null; @@ -1379,15 +1422,50 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, mTransitions.startTransition(TRANSIT_PIP, wct, null /* handler */); // We need to clear the WCT to send finishWCT=null for Recents. wct.clear(); + + if (Flags.enableShellTopTaskTracking()) { + // In this case, we've already started the PIP transition, so we can + // clean up immediately + mPendingRunnerFinishCb = runnerFinishCb; + onFinishInner(null); + return; + } } } - mPipTaskId = -1; - mPipTask = null; - mPipTransaction = null; } } + + if (Flags.enableShellTopTaskTracking()) { + if (!wct.isEmpty()) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.finishInner: " + + "Queuing TRANSIT_END_RECENTS_TRANSITION", mInstanceId); + mPendingRunnerFinishCb = runnerFinishCb; + mPendingFinishTransition = mTransitions.startTransition( + TRANSIT_END_RECENTS_TRANSITION, wct, + new PendingFinishTransitionHandler()); + } else { + // If there's no work to do, just go ahead and clean up + mPendingRunnerFinishCb = runnerFinishCb; + onFinishInner(null /* wct */); + } + } else { + mPendingRunnerFinishCb = runnerFinishCb; + onFinishInner(wct); + } + } + + /** + * Runs the actual logic to finish the recents transition. + */ + private void onFinishInner(@Nullable WindowContainerTransaction wct) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.finishInner: Completing finish", mInstanceId); + final Transitions.TransitionFinishCallback finishCb = mFinishCB; + final IResultReceiver runnerFinishCb = mPendingRunnerFinishCb; + cleanUp(); - finishCB.onTransitionFinished(wct.isEmpty() ? null : wct); + finishCb.onTransitionFinished(wct); if (runnerFinishCb != null) { try { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, @@ -1472,6 +1550,40 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, } }); } + + /** + * A temporary transition handler used with the pending finish transition, which runs the + * cleanup/finish logic once the pending transition is merged/handled. + * This is only initialized if Flags.enableShellTopTaskTracking() is enabled. + */ + private class PendingFinishTransitionHandler implements Transitions.TransitionHandler { + @Override + public boolean startAnimation(@NonNull IBinder transition, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + return false; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + return null; + } + + @Override + public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, + @Nullable SurfaceControl.Transaction finishTransaction) { + // Once we have merged (or not if the WCT didn't result in any changes), then we can + // run the pending finish logic + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.onTransitionConsumed: " + + "Consumed pending finish transition", mInstanceId); + onFinishInner(null /* wct */); + } + }; }; /** Utility class to track the state of a task as-seen by recents. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 1d456aed5f4d..3f191497e1ed 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -203,6 +203,12 @@ public class Transitions implements RemoteCallable<Transitions>, /** Transition type to minimize a task. */ public static final int TRANSIT_MINIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 20; + /** Transition to start the recents transition */ + public static final int TRANSIT_START_RECENTS_TRANSITION = TRANSIT_FIRST_CUSTOM + 21; + + /** Transition to end the recents transition */ + public static final int TRANSIT_END_RECENTS_TRANSITION = TRANSIT_FIRST_CUSTOM + 22; + /** Transition type for desktop mode transitions. */ public static final int TRANSIT_DESKTOP_MODE_TYPES = WindowManager.TRANSIT_FIRST_CUSTOM + 100; @@ -1875,6 +1881,8 @@ public class Transitions implements RemoteCallable<Transitions>, case TRANSIT_SPLIT_PASSTHROUGH -> "SPLIT_PASSTHROUGH"; case TRANSIT_CLEANUP_PIP_EXIT -> "CLEANUP_PIP_EXIT"; case TRANSIT_MINIMIZE -> "MINIMIZE"; + case TRANSIT_START_RECENTS_TRANSITION -> "START_RECENTS_TRANSITION"; + case TRANSIT_END_RECENTS_TRANSITION -> "END_RECENTS_TRANSITION"; default -> ""; }; return typeStr + "(FIRST_CUSTOM+" + (transitType - TRANSIT_FIRST_CUSTOM) + ")"; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index a6034664af5a..20481f25fa5c 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -465,6 +465,31 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { return false; } + /** + * This ensures that all changes for previously transient-hide containers are flagged such that + * they will report changes and be included in this transition. + */ + void updateChangesForRestoreTransientHideTasks(Transition transientLaunchTransition) { + if (transientLaunchTransition.mTransientHideTasks == null) { + // Skip if the transient-launch transition has no transient-hide tasks + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Skipping update changes for restore transient hide tasks"); + return; + } + + // For each change, if it was previously transient-hidden, then we should force a flag to + // ensure that it is included in the next transition + for (int i = 0; i < mChanges.size(); i++) { + final WindowContainer container = mChanges.keyAt(i); + if (transientLaunchTransition.isInTransientHide(container)) { + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Force update transient hide task for restore %d: %s", mSyncId, container); + final ChangeInfo info = mChanges.valueAt(i); + info.mRestoringTransientHide = true; + } + } + } + /** Returns {@code true} if the task should keep visible if this is a transient transition. */ boolean isTransientVisible(@NonNull Task task) { if (mTransientLaunches == null) return false; @@ -3478,6 +3503,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // State tracking boolean mExistenceChanged = false; + // This state indicates that we are restoring transient order as a part of an + // end-transition. Because the visibility for transient hide containers has not actually + // changed, we need to ensure that hasChanged() still reports the relevant changes + boolean mRestoringTransientHide = false; // before change state boolean mVisible; int mWindowingMode; @@ -3552,7 +3581,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { || !mContainer.getBounds().equals(mAbsoluteBounds) || mRotation != mContainer.getWindowConfiguration().getRotation() || mDisplayId != getDisplayId(mContainer) - || (mFlags & ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP) != 0; + || (mFlags & ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP) != 0 + // If we are restoring transient-hide containers, then we should consider them + // important for the transition as well (their requested visibilities would not + // have changed for the checks below to consider it). + || mRestoringTransientHide; } @TransitionInfo.TransitionMode @@ -3565,6 +3598,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } final boolean nowVisible = wc.isVisibleRequested(); if (nowVisible == mVisible) { + if (mRestoringTransientHide) { + // The requested visibility has not changed for transient-hide containers, but + // we are restoring them so we should considering them moving to front again + return TRANSIT_TO_FRONT; + } return TRANSIT_CHANGE; } if (mExistenceChanged) { diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 87bdfa4f5d75..143d1b72fff9 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -37,6 +37,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.util.ArrayMap; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -524,6 +525,23 @@ class TransitionController { return false; } + /** + * @return A pair of the transition and restore-behind target for the given {@param container}. + * @param container An ancestor of a transient-launch activity + */ + @Nullable + Pair<Transition, Task> getTransientLaunchTransitionAndTarget( + @NonNull WindowContainer container) { + for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { + final Transition transition = mPlayingTransitions.get(i); + final Task restoreBehindTask = transition.getTransientLaunchRestoreTarget(container); + if (restoreBehindTask != null) { + return new Pair<>(transition, restoreBehindTask); + } + } + return null; + } + /** Returns {@code true} if the display contains a transient-launch transition. */ boolean hasTransientLaunch(@NonNull DisplayContent dc) { if (mCollectingTransition != null && mCollectingTransition.hasTransientLaunch() diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index dac8f69a4cae..ead12826c263 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -111,6 +111,7 @@ import android.os.RemoteException; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Pair; import android.util.Slog; import android.view.RemoteAnimationAdapter; import android.view.SurfaceControl; @@ -1375,16 +1376,56 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } case HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER: { - if (!chain.isFinishing()) break; + if (!com.android.wm.shell.Flags.enableShellTopTaskTracking()) { + // Only allow restoring transient order when finishing a transition + if (!chain.isFinishing()) break; + } + // Validate the container final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); - if (container == null) break; + if (container == null) { + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Restoring transient order: invalid container"); + break; + } final Task thisTask = container.asActivityRecord() != null ? container.asActivityRecord().getTask() : container.asTask(); - if (thisTask == null) break; - final Task restoreAt = chain.mTransition.getTransientLaunchRestoreTarget(container); - if (restoreAt == null) break; + if (thisTask == null) { + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Restoring transient order: invalid task"); + break; + } + + // Find the task to restore behind + final Pair<Transition, Task> transientRestore = + mTransitionController.getTransientLaunchTransitionAndTarget(container); + if (transientRestore == null) { + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Restoring transient order: no restore task"); + break; + } + final Transition transientLaunchTransition = transientRestore.first; + final Task restoreAt = transientRestore.second; + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Restoring transient order: restoring behind task=%d", restoreAt.mTaskId); + + // Restore the position of the given container behind the target task final TaskDisplayArea taskDisplayArea = thisTask.getTaskDisplayArea(); taskDisplayArea.moveRootTaskBehindRootTask(thisTask.getRootTask(), restoreAt); + + if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) { + // Because we are in a transient launch transition, the requested visibility of + // tasks does not actually change for the transient-hide tasks, but we do want + // the restoration of these transient-hide tasks to top to be a part of this + // finish transition + final Transition collectingTransition = + mTransitionController.getCollectingTransition(); + if (collectingTransition != null) { + collectingTransition.updateChangesForRestoreTransientHideTasks( + transientLaunchTransition); + } + } + + effects |= TRANSACT_EFFECTS_LIFECYCLE; break; } case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: { |