Merge "3b/ Migrate away from finishWCT usage in recents transition" into main
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 40065b9..9016c45 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 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 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 @@
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 @@
"RecentsTransitionHandler.mergeAnimation: no controller found");
return;
}
- controller.merge(info, t, finishCallback);
+ controller.merge(info, t, mergeTarget, finishCallback);
}
@Override
@@ -367,6 +373,8 @@
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 @@
// 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 @@
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 @@
// 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 @@
}
}
+ /**
+ * 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 @@
return;
}
- if (mFinishCB == null) {
+ if (mFinishCB == null
+ || (Flags.enableShellTopTaskTracking() && mPendingFinishTransition != null)) {
Slog.e(TAG, "Duplicate call to finish");
return;
}
@@ -1254,19 +1293,22 @@
&& !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 @@
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 @@
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 @@
}
});
}
+
+ /**
+ * 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 1d456ae..3f19149 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 @@
/** 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 @@
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 a603466..20481f2 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 @@
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 @@
// 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 @@
|| !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 @@
}
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 87bdfa4..143d1b7 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.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 @@
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 dac8f69..ead1282 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.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 @@
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: {