Merge "Revert "Create a type for tracking chains of Actions requested of WM"" into main
diff --git a/services/core/java/com/android/server/wm/ActionChain.java b/services/core/java/com/android/server/wm/ActionChain.java
deleted file mode 100644
index c697d33..0000000
--- a/services/core/java/com/android/server/wm/ActionChain.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.util.Slog;
-
-import com.android.window.flags.Flags;
-
-/**
- * Represents a chain of WM actions where each action is "caused by" the prior action (except the
- * first one of course). A whole chain is associated with one Transition (in fact, the purpose
- * of this object is to communicate, to all callees, which transition they are part of).
- *
- * A single action is defined as "one logical thing requested of WM". This usually corresponds to
- * each ingress-point into the process. For example, when starting an activity:
- *   * the first action is to pause the current/top activity.
- *       At this point, control leaves the process while the activity pauses.
- *   * Then WM receives completePause (a new ingress). This is a new action that gets linked
- *       to the prior action. This action involves resuming the next activity, at which point,
- *       control leaves the process again.
- *   * Eventually, when everything is done, we will have formed a chain of actions.
- *
- * We don't technically need to hold onto each prior action in the chain once a new action has
- * been linked to the same transition; however, keeping the whole chain enables improved
- * debugging and the ability to detect anomalies.
- */
-public class ActionChain {
-    private static final String TAG = "TransitionChain";
-
-    /**
-     * Normal link type. This means the action was expected and is properly linked to the
-     * current chain.
-     */
-    static final int TYPE_NORMAL = 0;
-
-    /**
-     * This is the "default" link. It means we haven't done anything to properly track this case
-     * so it may or may not be correct. It represents the behavior as if there was no tracking.
-     *
-     * Any type that has "default" behavior uses the global "collecting transition" if it exists,
-     * otherwise it doesn't use any transition.
-     */
-    static final int TYPE_DEFAULT = 1;
-
-    /**
-     * This means the action was performed via a legacy code-path. These should be removed
-     * eventually. This will have the "default" behavior.
-     */
-    static final int TYPE_LEGACY = 2;
-
-    /** This is for a test. */
-    static final int TYPE_TEST = 3;
-
-    /** This is finishing a transition. Collection isn't supported during this. */
-    static final int TYPE_FINISH = 4;
-
-    /**
-     * Something unexpected happened so this action was started to recover from the unexpected
-     * state. This means that a "real" chain-link couldn't be determined. For now, the behavior of
-     * this is the same as "default".
-     */
-    static final int TYPE_FAILSAFE = 5;
-
-    /**
-     * Types of chain links (ie. how is this action associated with the chain it is linked to).
-     * @hide
-     */
-    @IntDef(prefix = { "TYPE_" }, value = {
-            TYPE_NORMAL,
-            TYPE_DEFAULT,
-            TYPE_LEGACY,
-            TYPE_TEST,
-            TYPE_FINISH,
-            TYPE_FAILSAFE
-    })
-    public @interface LinkType {}
-
-    /** Identifies the entry-point of this action. */
-    @NonNull
-    final String mSource;
-
-    /** Reference to ATMS. TEMPORARY! ONLY USE THIS WHEN tracker_plumbing flag is DISABLED! */
-    @Nullable
-    ActivityTaskManagerService mTmpAtm;
-
-    /** The transition that this chain's changes belong to. */
-    @Nullable
-    Transition mTransition;
-
-    /** The previous action in the chain. */
-    @Nullable
-    ActionChain mPrevious = null;
-
-    /** Classification of how this action is connected to the chain. */
-    @LinkType int mType = TYPE_NORMAL;
-
-    /** When this Action started. */
-    long mCreateTimeMs;
-
-    private ActionChain(String source, @LinkType int type, Transition transit) {
-        mSource = source;
-        mCreateTimeMs = System.currentTimeMillis();
-        mType = type;
-        mTransition = transit;
-        if (mTransition != null) {
-            mTransition.recordChain(this);
-        }
-    }
-
-    private Transition getTransition() {
-        if (!Flags.transitTrackerPlumbing()) {
-            return mTmpAtm.getTransitionController().getCollectingTransition();
-        }
-        return mTransition;
-    }
-
-    boolean isFinishing() {
-        return mType == TYPE_FINISH;
-    }
-
-    /**
-     * Some common checks to determine (and report) whether this chain has a collecting transition.
-     */
-    private boolean expectCollecting() {
-        if (getTransition() == null) {
-            Slog.e(TAG, "Can't collect into a chain with no transition");
-            return false;
-        }
-        if (isFinishing()) {
-            Slog.e(TAG, "Trying to collect into a finished transition");
-            return false;
-        }
-        if (mTransition.mController.getCollectingTransition() != mTransition) {
-            Slog.e(TAG, "Mismatch between current collecting ("
-                    + mTransition.mController.getCollectingTransition() + ") and chain ("
-                    + mTransition + ")");
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Helper to collect a container into the associated transition. This will automatically do
-     * nothing if the chain isn't associated with a collecting transition.
-     */
-    void collect(@NonNull WindowContainer wc) {
-        if (!wc.mTransitionController.isShellTransitionsEnabled()) return;
-        if (!expectCollecting()) return;
-        getTransition().collect(wc);
-    }
-
-    /**
-     * An interface for creating and tracking action chains.
-     */
-    static class Tracker {
-        private final ActivityTaskManagerService mAtm;
-
-        Tracker(ActivityTaskManagerService atm) {
-            mAtm = atm;
-        }
-
-        private ActionChain makeChain(String source, @LinkType int type, Transition transit) {
-            final ActionChain out = new ActionChain(source, type, transit);
-            if (!Flags.transitTrackerPlumbing()) {
-                out.mTmpAtm = mAtm;
-            }
-            return out;
-        }
-
-        private ActionChain makeChain(String source, @LinkType int type) {
-            return makeChain(source, type,
-                    mAtm.getTransitionController().getCollectingTransition());
-        }
-
-        /**
-         * Starts tracking a normal action.
-         * @see #TYPE_NORMAL
-         */
-        @NonNull
-        ActionChain start(String source, Transition transit) {
-            return makeChain(source, TYPE_NORMAL, transit);
-        }
-
-        /** @see #TYPE_DEFAULT */
-        @NonNull
-        ActionChain startDefault(String source) {
-            return makeChain(source, TYPE_DEFAULT);
-        }
-
-        /**
-         * Starts tracking an action that finishes a transition.
-         * @see #TYPE_NORMAL
-         */
-        @NonNull
-        ActionChain startFinish(String source, Transition finishTransit) {
-            return makeChain(source, TYPE_FINISH, finishTransit);
-        }
-
-        /** @see #TYPE_LEGACY */
-        @NonNull
-        ActionChain startLegacy(String source) {
-            return makeChain(source, TYPE_LEGACY, null);
-        }
-
-        /** @see #TYPE_FAILSAFE */
-        @NonNull
-        ActionChain startFailsafe(String source) {
-            return makeChain(source, TYPE_FAILSAFE);
-        }
-    }
-
-    /** Helpers for usage in tests. */
-    @NonNull
-    static ActionChain test() {
-        return new ActionChain("test", TYPE_TEST, null /* transition */);
-    }
-
-    @NonNull
-    static ActionChain testFinish(Transition toFinish) {
-        return new ActionChain("test", TYPE_FINISH, toFinish);
-    }
-}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 03c7fd1..0f108c5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -795,7 +795,6 @@
     WindowOrganizerController mWindowOrganizerController;
     TaskOrganizerController mTaskOrganizerController;
     TaskFragmentOrganizerController mTaskFragmentOrganizerController;
-    ActionChain.Tracker mChainTracker;
 
     @Nullable
     private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
@@ -870,7 +869,6 @@
         mInternal = new LocalService();
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
         mWindowOrganizerController = new WindowOrganizerController(this);
-        mChainTracker = new ActionChain.Tracker(this);
         mTaskOrganizerController = mWindowOrganizerController.mTaskOrganizerController;
         mTaskFragmentOrganizerController =
                 mWindowOrganizerController.mTaskFragmentOrganizerController;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 82ede7e..e25db7e 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -181,7 +181,7 @@
     final @TransitionType int mType;
     private int mSyncId = -1;
     private @TransitionFlags int mFlags;
-    final TransitionController mController;
+    private final TransitionController mController;
     private final BLASTSyncEngine mSyncEngine;
     private final Token mToken;
 
@@ -329,9 +329,6 @@
      */
     ArrayList<ActivityRecord> mConfigAtEndActivities = null;
 
-    /** The current head of the chain of actions related to this transition. */
-    ActionChain mChainHead = null;
-
     @VisibleForTesting
     Transition(@TransitionType int type, @TransitionFlags int flags,
             TransitionController controller, BLASTSyncEngine syncEngine) {
@@ -1210,14 +1207,10 @@
      * The transition has finished animating and is ready to finalize WM state. This should not
      * be called directly; use {@link TransitionController#finishTransition} instead.
      */
-    void finishTransition(@NonNull ActionChain chain) {
+    void finishTransition() {
         if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER) && mIsPlayerEnabled) {
             asyncTraceEnd(System.identityHashCode(this));
         }
-        if (!chain.isFinishing()) {
-            throw new IllegalStateException("Can't finish on a non-finishing transition "
-                    + chain.mTransition);
-        }
         mLogger.mFinishTimeNs = SystemClock.elapsedRealtimeNanos();
         mController.mLoggerHandler.post(mLogger::logOnFinish);
         mController.mTransitionTracer.logFinishedTransition(this);
@@ -2170,7 +2163,7 @@
         if (mFinishTransaction != null) {
             mFinishTransaction.apply();
         }
-        mController.finishTransition(mController.mAtm.mChainTracker.startFinish("clean-up", this));
+        mController.finishTransition(this);
     }
 
     private void cleanUpInternal() {
@@ -3386,11 +3379,6 @@
         return false;
     }
 
-    void recordChain(@NonNull ActionChain chain) {
-        chain.mPrevious = mChainHead;
-        mChainHead = chain;
-    }
-
     @VisibleForTesting
     static class ChangeInfo {
         private static final int FLAG_NONE = 0;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 56a24dd..9bbf102 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -52,8 +52,8 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.ProtoLog;
 import com.android.internal.protolog.ProtoLogGroup;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.FgThread;
 import com.android.window.flags.Flags;
 
@@ -921,12 +921,7 @@
     }
 
     /** @see Transition#finishTransition */
-    void finishTransition(@NonNull ActionChain chain) {
-        if (!chain.isFinishing()) {
-            throw new IllegalStateException("Can't finish on a non-finishing transition "
-                    + chain.mTransition);
-        }
-        final Transition record = chain.mTransition;
+    void finishTransition(Transition record) {
         // It is usually a no-op but make sure that the metric consumer is removed.
         mTransitionMetricsReporter.reportAnimationStart(record.getToken(), 0 /* startTime */);
         // It is a no-op if the transition did not change the display.
@@ -942,7 +937,7 @@
             mTrackCount = 0;
         }
         updateRunningRemoteAnimation(record, false /* isPlaying */);
-        record.finishTransition(chain);
+        record.finishTransition();
         for (int i = mAnimatingExitWindows.size() - 1; i >= 0; i--) {
             final WindowState w = mAnimatingExitWindows.get(i);
             if (w.mAnimatingExit && w.mHasSurface && !w.inTransition()) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 60ccdc7..e1e64ee 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -223,8 +223,7 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final ActionChain chain = mService.mChainTracker.startLegacy("applyTransactLegacy");
-                applyTransaction(t, -1 /*syncId*/, chain, caller);
+                applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -243,8 +242,7 @@
         try {
             synchronized (mGlobalLock) {
                 if (callback == null) {
-                    final ActionChain chain = mService.mChainTracker.startLegacy("applySyncLegacy");
-                    applyTransaction(t, -1 /* syncId*/, chain, caller);
+                    applyTransaction(t, -1 /* syncId*/, null /*transition*/, caller);
                     return -1;
                 }
 
@@ -264,15 +262,13 @@
                 final int syncId = syncGroup.mSyncId;
                 if (mTransitionController.isShellTransitionsEnabled()) {
                     mTransitionController.startLegacySyncOrQueue(syncGroup, (deferred) -> {
-                        applyTransaction(t, syncId, mService.mChainTracker.startLegacy(
-                                "applySyncLegacy"), caller, deferred);
+                        applyTransaction(t, syncId, null /* transition */, caller, deferred);
                         setSyncReady(syncId);
                     });
                 } else {
                     if (!mService.mWindowManager.mSyncEngine.hasActiveSync()) {
                         mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup);
-                        applyTransaction(t, syncId, mService.mChainTracker.startLegacy(
-                                "applySyncLegacy"), caller);
+                        applyTransaction(t, syncId, null /*transition*/, caller);
                         setSyncReady(syncId);
                     } else {
                         // Because the BLAST engine only supports one sync at a time, queue the
@@ -280,8 +276,7 @@
                         mService.mWindowManager.mSyncEngine.queueSyncSet(
                                 () -> mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup),
                                 () -> {
-                                    applyTransaction(t, syncId, mService.mChainTracker.startLegacy(
-                                            "applySyncLegacy"), caller);
+                                    applyTransaction(t, syncId, null /*transition*/, caller);
                                     setSyncReady(syncId);
                                 });
                     }
@@ -318,8 +313,7 @@
                         throw new IllegalArgumentException("Can't use legacy transitions in"
                                 + " compatibility mode with no WCT.");
                     }
-                    applyTransaction(t, -1 /* syncId */,
-                            mService.mChainTracker.startLegacy("wrongLegacyTransit"), caller);
+                    applyTransaction(t, -1 /* syncId */, null, caller);
                     return null;
                 }
                 final WindowContainerTransaction wct =
@@ -340,11 +334,10 @@
                     nextTransition.calcParallelCollectType(wct);
                     mTransitionController.startCollectOrQueue(nextTransition,
                             (deferred) -> {
-                                final ActionChain chain = mService.mChainTracker.start(
-                                        "startNewTransit", nextTransition);
                                 nextTransition.start();
                                 nextTransition.mLogger.mStartWCT = wct;
-                                applyTransaction(wct, -1 /* syncId */, chain, caller, deferred);
+                                applyTransaction(wct, -1 /* syncId */, nextTransition, caller,
+                                        deferred);
                                 wctApplied.meet();
                                 if (needsSetReady) {
                                     setAllReadyIfNeeded(nextTransition, wct);
@@ -358,9 +351,7 @@
                     Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
                             + " means Shell took too long to respond to a request. WM State may be"
                             + " incorrect now, please file a bug");
-                    final ActionChain chain = mService.mChainTracker.startFailsafe("startTransit");
-                    chain.mTransition = null;
-                    applyTransaction(wct, -1 /*syncId*/, chain, caller);
+                    applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller);
                     return transition.getToken();
                 }
                 // Currently, application of wct can span multiple looper loops (ie.
@@ -376,20 +367,16 @@
                 if (transition.shouldApplyOnDisplayThread()) {
                     mService.mH.post(() -> {
                         synchronized (mService.mGlobalLock) {
-                            final ActionChain chain = mService.mChainTracker.start(
-                                    "startTransit", transition);
                             transition.start();
-                            applyTransaction(wct, -1 /* syncId */, chain, caller);
+                            applyTransaction(wct, -1 /* syncId */, transition, caller);
                             if (wctApplied != null) {
                                 wctApplied.meet();
                             }
                         }
                     });
                 } else {
-                    final ActionChain chain = mService.mChainTracker.start("startTransit",
-                            transition);
                     transition.start();
-                    applyTransaction(wct, -1 /* syncId */, chain, caller);
+                    applyTransaction(wct, -1 /* syncId */, transition, caller);
                     if (wctApplied != null) {
                         wctApplied.meet();
                     }
@@ -488,8 +475,7 @@
                 dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */,
                         false /* isActivityEmbedding */);
                 syncId = startSyncWithOrganizer(callback);
-                applyTransaction(t, syncId, mService.mChainTracker.startLegacy("legacyTransit"),
-                        caller);
+                applyTransaction(t, syncId, null /* transition */, caller);
                 setSyncReady(syncId);
             }
         } finally {
@@ -507,8 +493,6 @@
         try {
             synchronized (mGlobalLock) {
                 final Transition transition = Transition.fromBinder(transitionToken);
-                final ActionChain chain =
-                        mService.mChainTracker.startFinish("finishTransit", transition);
                 // apply the incoming transaction before finish in case it alters the visibility
                 // of the participants.
                 if (t != null) {
@@ -516,9 +500,9 @@
                     // changes of the transition participants will only set visible-requested
                     // and still let finishTransition handle the participants.
                     mTransitionController.mFinishingTransition = transition;
-                    applyTransaction(t, -1 /* syncId */, chain, caller);
+                    applyTransaction(t, -1 /* syncId */, null /*transition*/, caller, transition);
                 }
-                mTransitionController.finishTransition(chain);
+                mTransitionController.finishTransition(transition);
                 mTransitionController.mFinishingTransition = null;
             }
         } finally {
@@ -553,10 +537,9 @@
         final CallerInfo caller = new CallerInfo();
         final long ident = Binder.clearCallingIdentity();
         try {
-            if (!mTransitionController.isShellTransitionsEnabled()) {
+            if (mTransitionController.getTransitionPlayer() == null) {
                 // No need to worry about transition when Shell transition is not enabled.
-                applyTransaction(wct, -1 /* syncId */,
-                        mService.mChainTracker.startLegacy("legacyTFTransact"), caller);
+                applyTransaction(wct, -1 /* syncId */, null /* transition */, caller);
                 return;
             }
 
@@ -565,8 +548,8 @@
                 // Although there is an active sync, we want to apply the transaction now.
                 // TODO(b/232042367) Redesign the organizer update on activity callback so that we
                 // we will know about the transition explicitly.
-                final ActionChain chain = mService.mChainTracker.startDefault("tfTransact");
-                if (chain.mTransition == null) {
+                final Transition transition = mTransitionController.getCollectingTransition();
+                if (transition == null) {
                     // This should rarely happen, and we should try to avoid using
                     // {@link #applySyncTransaction} with Shell transition.
                     // We still want to apply and merge the transaction to the active sync
@@ -576,7 +559,7 @@
                                     + " because there is an ongoing sync for"
                                     + " applySyncTransaction().");
                 }
-                applyTransaction(wct, -1 /* syncId */, chain, caller);
+                applyTransaction(wct, -1 /* syncId */, transition, caller);
                 return;
             }
 
@@ -587,9 +570,8 @@
                     transition.abort();
                     return;
                 }
-                final ActionChain chain = mService.mChainTracker.start("tfTransact", transition);
-                final int effects = applyTransaction(wct, -1 /* syncId */, chain, caller, deferred);
-                if (effects == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) {
+                if (applyTransaction(wct, -1 /* syncId */, transition, caller, deferred)
+                        == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) {
                     transition.abort();
                     return;
                 }
@@ -604,10 +586,15 @@
     }
 
     private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
-            @NonNull ActionChain chain, @NonNull CallerInfo caller, boolean deferred) {
+            @Nullable Transition transition, @NonNull CallerInfo caller) {
+        return applyTransaction(t, syncId, transition, caller, null /* finishTransition */);
+    }
+
+    private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
+            @Nullable Transition transition, @NonNull CallerInfo caller, boolean deferred) {
         if (deferred) {
             try {
-                return applyTransaction(t, syncId, chain, caller);
+                return applyTransaction(t, syncId, transition, caller);
             } catch (RuntimeException e) {
                 // If the transaction is deferred, the caller could be from TransitionController
                 // #tryStartCollectFromQueue that executes on system's worker thread rather than
@@ -617,17 +604,19 @@
             }
             return TRANSACT_EFFECTS_NONE;
         }
-        return applyTransaction(t, syncId, chain, caller);
+        return applyTransaction(t, syncId, transition, caller);
     }
 
     /**
      * @param syncId If non-null, this will be a sync-transaction.
-     * @param chain A lifecycle-chain to acculumate changes into.
+     * @param transition A transition to collect changes into.
      * @param caller Info about the calling process.
+     * @param finishTransition The transition that is currently being finished.
      * @return The effects of the window container transaction.
      */
     private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
-            @NonNull ActionChain chain, @NonNull CallerInfo caller) {
+            @Nullable Transition transition, @NonNull CallerInfo caller,
+            @Nullable Transition finishTransition) {
         int effects = TRANSACT_EFFECTS_NONE;
         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
         mService.deferWindowLayout();
@@ -635,21 +624,20 @@
         boolean deferResume = true;
         mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
         boolean deferTransitionReady = false;
-        if (chain.mTransition != null && !t.isEmpty() && !chain.isFinishing()) {
-            if (chain.mTransition.isCollecting()) {
+        if (transition != null && !t.isEmpty()) {
+            if (transition.isCollecting()) {
                 deferTransitionReady = true;
-                chain.mTransition.deferTransitionReady();
+                transition.deferTransitionReady();
             } else {
                 Slog.w(TAG, "Transition is not collecting when applyTransaction."
-                        + " transition=" + chain.mTransition + " state="
-                        + chain.mTransition.getState());
-                chain.mTransition = null;
+                        + " transition=" + transition + " state=" + transition.getState());
+                transition = null;
             }
         }
         try {
             final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
-            if (chain.mTransition != null) {
-                chain.mTransition.applyDisplayChangeIfNeeded(haveConfigChanges);
+            if (transition != null) {
+                transition.applyDisplayChangeIfNeeded(haveConfigChanges);
                 if (!haveConfigChanges.isEmpty()) {
                     effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
                 }
@@ -657,7 +645,7 @@
             final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
             final int hopSize = hops.size();
             Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries;
-            if (chain.mTransition != null) {
+            if (transition != null) {
                 // Mark any config-at-end containers before applying config changes so that
                 // the config changes don't dispatch to client.
                 entries = t.getChanges().entrySet().iterator();
@@ -667,7 +655,7 @@
                     if (!entry.getValue().getConfigAtTransitionEnd()) continue;
                     final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
                     if (wc == null || !wc.isAttached()) continue;
-                    chain.mTransition.setConfigAtEnd(wc);
+                    transition.setConfigAtEnd(wc);
                 }
             }
             entries = t.getChanges().entrySet().iterator();
@@ -684,13 +672,15 @@
                 if (syncId >= 0) {
                     addToSyncSet(syncId, wc);
                 }
-                chain.collect(wc);
+                if (transition != null) transition.collect(wc);
 
                 if ((entry.getValue().getChangeMask()
                         & WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) {
                     // Disable entering pip (eg. when recents pretends to finish itself)
-                    if (chain.mTransition != null) {
-                        chain.mTransition.setCanPipOnFinish(false /* canPipOnFinish */);
+                    if (finishTransition != null) {
+                        finishTransition.setCanPipOnFinish(false /* canPipOnFinish */);
+                    } else if (transition != null) {
+                        transition.setCanPipOnFinish(false /* canPipOnFinish */);
                     }
                 }
                 // A bit hacky, but we need to detect "remove PiP" so that we can "wrap" the
@@ -738,9 +728,9 @@
             if (hopSize > 0) {
                 final boolean isInLockTaskMode = mService.isInLockTaskMode();
                 for (int i = 0; i < hopSize; ++i) {
-                    effects |= applyHierarchyOp(hops.get(i), effects, syncId, chain,
+                    effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
                             isInLockTaskMode, caller, t.getErrorCallbackToken(),
-                            t.getTaskFragmentOrganizer());
+                            t.getTaskFragmentOrganizer(), finishTransition);
                 }
             }
             // Queue-up bounds-change transactions for tasks which are now organized. Do
@@ -799,7 +789,7 @@
             }
         } finally {
             if (deferTransitionReady) {
-                chain.mTransition.continueTransitionReady();
+                transition.continueTransitionReady();
             }
             mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
             if (deferResume) {
@@ -1089,9 +1079,9 @@
     }
 
     private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
-            int syncId, @NonNull ActionChain chain, boolean isInLockTaskMode,
+            int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
             @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,
-            @Nullable ITaskFragmentOrganizer organizer) {
+            @Nullable ITaskFragmentOrganizer organizer, @Nullable Transition finishTransition) {
         final int type = hop.getType();
         switch (type) {
             case HIERARCHY_OP_TYPE_REMOVE_TASK: {
@@ -1161,7 +1151,7 @@
                 break;
             }
             case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: {
-                effects |= reparentChildrenTasksHierarchyOp(hop, chain.mTransition, syncId,
+                effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId,
                         isInLockTaskMode);
                 break;
             }
@@ -1214,13 +1204,13 @@
                 if (syncId >= 0) {
                     addToSyncSet(syncId, wc);
                 }
-                if (chain.mTransition != null) {
-                    chain.mTransition.collect(wc);
+                if (transition != null) {
+                    transition.collect(wc);
                     if (hop.isReparent()) {
                         if (wc.getParent() != null) {
                             // Collect the current parent. It's visibility may change as
                             // a result of this reparenting.
-                            chain.mTransition.collect(wc.getParent());
+                            transition.collect(wc.getParent());
                         }
                         if (hop.getNewParent() != null) {
                             final WindowContainer parentWc =
@@ -1229,7 +1219,7 @@
                                 Slog.e(TAG, "Can't resolve parent window from token");
                                 break;
                             }
-                            chain.mTransition.collect(parentWc);
+                            transition.collect(parentWc);
                         }
                     }
                 }
@@ -1243,8 +1233,8 @@
                 break;
             }
             case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION: {
-                effects |= applyTaskFragmentOperation(hop, chain, isInLockTaskMode,
-                        caller, errorCallbackToken, organizer);
+                effects |= applyTaskFragmentOperation(hop, transition, isInLockTaskMode, caller,
+                        errorCallbackToken, organizer);
                 break;
             }
             case HIERARCHY_OP_TYPE_PENDING_INTENT: {
@@ -1358,13 +1348,13 @@
                 break;
             }
             case HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER: {
-                if (!chain.isFinishing()) break;
+                if (finishTransition == null) break;
                 final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
                 if (container == null) break;
                 final Task thisTask = container.asActivityRecord() != null
                         ? container.asActivityRecord().getTask() : container.asTask();
                 if (thisTask == null) break;
-                final Task restoreAt = chain.mTransition.getTransientLaunchRestoreTarget(container);
+                final Task restoreAt = finishTransition.getTransientLaunchRestoreTarget(container);
                 if (restoreAt == null) break;
                 final TaskDisplayArea taskDisplayArea = thisTask.getTaskDisplayArea();
                 taskDisplayArea.moveRootTaskBehindRootTask(thisTask.getRootTask(), restoreAt);
@@ -1454,7 +1444,7 @@
      *         {@link #TRANSACT_EFFECTS_LIFECYCLE} or {@link #TRANSACT_EFFECTS_CLIENT_CONFIG}.
      */
     private int applyTaskFragmentOperation(@NonNull WindowContainerTransaction.HierarchyOp hop,
-            @NonNull ActionChain chain, boolean isInLockTaskMode, @NonNull CallerInfo caller,
+            @Nullable Transition transition, boolean isInLockTaskMode, @NonNull CallerInfo caller,
             @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer) {
         if (!validateTaskFragmentOperation(hop, errorCallbackToken, organizer)) {
             return TRANSACT_EFFECTS_NONE;
@@ -1477,7 +1467,7 @@
                     break;
                 }
                 createTaskFragment(taskFragmentCreationParams, errorCallbackToken, caller,
-                        chain.mTransition);
+                        transition);
                 break;
             }
             case OP_TYPE_DELETE_TASK_FRAGMENT: {
@@ -1494,7 +1484,7 @@
                         break;
                     }
                 }
-                effects |= deleteTaskFragment(taskFragment, chain.mTransition);
+                effects |= deleteTaskFragment(taskFragment, transition);
                 break;
             }
             case OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
@@ -1543,14 +1533,14 @@
                             opType, exception);
                     break;
                 }
-                if (chain.mTransition != null) {
-                    chain.collect(activity);
+                if (transition != null) {
+                    transition.collect(activity);
                     if (activity.getParent() != null) {
                         // Collect the current parent. Its visibility may change as a result of
                         // this reparenting.
-                        chain.collect(activity.getParent());
+                        transition.collect(activity.getParent());
                     }
-                    chain.collect(taskFragment);
+                    transition.collect(taskFragment);
                 }
                 activity.reparent(taskFragment, POSITION_TOP);
                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
@@ -1706,8 +1696,8 @@
                 // If any TaskFragment in the Task is collected by the transition, we make the decor
                 // surface visible in sync with the TaskFragment transition. Otherwise, we make the
                 // decor surface visible immediately.
-                final TaskFragment syncTaskFragment = chain.mTransition != null
-                        ? task.getTaskFragment(chain.mTransition.mParticipants::contains)
+                final TaskFragment syncTaskFragment = transition != null
+                        ? task.getTaskFragment(transition.mParticipants::contains)
                         : null;
 
                 if (syncTaskFragment != null) {
@@ -1759,7 +1749,7 @@
                     // The decor surface boost/unboost must be applied after the transition is
                     // completed. Otherwise, the decor surface could be moved before Shell completes
                     // the transition, causing flicker.
-                    runAfterTransition(chain.mTransition, task::commitDecorSurfaceBoostedState);
+                    runAfterTransition(transition, task::commitDecorSurfaceBoostedState);
                 }
                 break;
             }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 52a80b0..56fca31 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1251,7 +1251,7 @@
         final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
         app.mTransitionController.requestStartTransition(transition, app.getTask(),
                 null /* remoteTransition */, null /* displayChange */);
-        transition.collectExistenceChange(app.getTask());
+        app.mTransitionController.collectExistenceChange(app.getTask());
         mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
         final AsyncRotationController asyncRotationController =
                 mDisplayContent.getAsyncRotationController();
@@ -1416,8 +1416,7 @@
         activity1.setVisibleRequested(false);
         activity2.setVisibleRequested(true);
 
-        final ActionChain chain = ActionChain.testFinish(null);
-        openTransition.finishTransition(chain);
+        openTransition.finishTransition();
 
         // We finished the openTransition. Even though activity1 is visibleRequested=false, since
         // the closeTransition animation hasn't played yet, make sure that we didn't commit
@@ -1430,7 +1429,7 @@
         // normally.
         mWm.mSyncEngine.abort(closeTransition.getSyncId());
 
-        closeTransition.finishTransition(chain);
+        closeTransition.finishTransition();
 
         assertFalse(activity1.isVisible());
         assertTrue(activity2.isVisible());
@@ -1450,7 +1449,7 @@
         activity1.setState(ActivityRecord.State.INITIALIZING, "test");
         activity1.mLaunchTaskBehind = true;
         mWm.mSyncEngine.abort(noChangeTransition.getSyncId());
-        noChangeTransition.finishTransition(chain);
+        noChangeTransition.finishTransition();
         assertTrue(activity1.mLaunchTaskBehind);
     }
 
@@ -1469,7 +1468,7 @@
         // We didn't call abort on the transition itself, so it will still run onTransactionReady
         // normally.
         mWm.mSyncEngine.abort(transition1.getSyncId());
-        transition1.finishTransition(ActionChain.testFinish(transition1));
+        transition1.finishTransition();
 
         verify(transitionEndedListener).run();
 
@@ -1531,7 +1530,7 @@
 
         verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2));
 
-        controller.finishTransition(ActionChain.testFinish(openTransition));
+        controller.finishTransition(openTransition);
 
         // We are now going to simulate closing task1 to return back to (open) task2.
         final Transition closeTransition = createTestTransition(TRANSIT_CLOSE, controller);
@@ -1596,7 +1595,7 @@
         doReturn(true).when(task1).isTranslucentForTransition();
         assertFalse(controller.canApplyDim(task1));
 
-        controller.finishTransition(ActionChain.testFinish(closeTransition));
+        controller.finishTransition(closeTransition);
         assertTrue(wasInFinishingTransition[0]);
         assertFalse(calledListenerOnOtherDisplay[0]);
         assertNull(controller.mFinishingTransition);
@@ -1652,7 +1651,7 @@
         // to avoid the latency to resume the current top, i.e. appB.
         assertTrue(controller.isTransientVisible(taskRecent));
         // The recent is paused after the transient transition is finished.
-        controller.finishTransition(ActionChain.testFinish(transition));
+        controller.finishTransition(transition);
         assertFalse(controller.isTransientVisible(taskRecent));
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 9602ae2..7652861 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -407,7 +407,7 @@
         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
         token.finishSync(t, token.getSyncGroup(), false /* cancel */);
         transit.onTransactionReady(transit.getSyncId(), t);
-        dc.mTransitionController.finishTransition(ActionChain.testFinish(transit));
+        dc.mTransitionController.finishTransition(transit);
         assertFalse(wallpaperWindow.isVisible());
         assertFalse(token.isVisible());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index a215c0a..bcf4ebc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -2131,7 +2131,7 @@
         }
 
         public void finish() {
-            mController.finishTransition(ActionChain.testFinish(mLastTransit));
+            mController.finishTransition(mLastTransit);
         }
     }
 }