Each displays can have individual app transition.

Include below refectoring items to support per display AppTransition:

WMS / AM refectoring parts:
- Move AppTransition related stuff from WMS into DisplayContent.
- Move WMS.prepareAppTransition into DisplayWindowController.
- Move WMS.executeAppTransition to DisplayWindowController.
- Move ATM.isNextTransitionForward to DisplayWindowController.
- Move WMS.getPendingAppTransition to DisplayWindowController.
- Move WMS.overrideAppTransition like APIs to DisplayWindowController.
- Move ActivityRecord.applyOptionsLocked to AppContainerController.
- Support tracing all display's AppTransition status for
  DisplayContent.pendingLayoutChanges & window hierachy update.
- Modify logics for AppTransition related caller parts.
- Move WindowSurfacePlacer.handleAppTransitionReadyLocked related
  stuffs into added class AppTransitionController.

WM unit test parts:
- Add test case for verifying app transition state per display:
  - AppTransitionTests.testAppTransitionStateForMultiDisplay
  - AppTransitionTests.testCleanAppTransitionWhenTaskStackReparent
- Rename WindowSurfacePlacerTest to AppTransitionControllerTest since
  the test is related handle AppTransition flow.

Bug: 111362605
Test: go/wm-smoke
Test: atest ActivityManagerTransitionSelectionTests
Test: atest ActivityManagerMultiDisplayTests
Test: atest FrameworksServicesTests for DisplayContent / AppTransition
      related tests.
Change-Id: Ic1793aa794eb161bec31fda57847a6ba2ff4f84f
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fa9ae52..ba03034 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -65,6 +65,7 @@
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.DisplayContentProto.ABOVE_APP_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
 import static com.android.server.wm.DisplayContentProto.BELOW_APP_WINDOWS;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
@@ -80,6 +81,7 @@
 import static com.android.server.wm.DisplayContentProto.SURFACE_SIZE;
 import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
@@ -119,6 +121,7 @@
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
 
+import android.animation.AnimationHandler;
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -155,6 +158,7 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
@@ -228,6 +232,21 @@
     private boolean mTmpInitial;
     private int mMaxUiWidth;
 
+    final AppTransition mAppTransition;
+    final AppTransitionController mAppTransitionController;
+    boolean mSkipAppTransitionAnimation = false;
+
+    final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<>();
+    final ArraySet<AppWindowToken> mClosingApps = new ArraySet<>();
+    final UnknownAppVisibilityController mUnknownAppVisibilityController;
+    BoundsAnimationController mBoundsAnimationController;
+
+    /**
+     * List of clients without a transtiton animation that we notify once we are done
+     * transitioning since they won't be notified through the app window animator.
+     */
+    final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>();
+
     // Mapping from a token IBinder to a WindowToken object on this display.
     private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();
 
@@ -821,6 +840,15 @@
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
 
+        mAppTransition = new AppTransition(service.mContext, service, this);
+        mAppTransition.registerListenerLocked(service.mActivityManagerAppTransitionNotifier);
+        mAppTransitionController = new AppTransitionController(service, this);
+        mUnknownAppVisibilityController = new UnknownAppVisibilityController(service, this);
+
+        AnimationHandler animationHandler = new AnimationHandler();
+        mBoundsAnimationController = new BoundsAnimationController(service.mContext,
+                mAppTransition, SurfaceAnimationThread.getHandler(), animationHandler);
+
         // We use this as our arbitrary surface size for buffer-less parents
         // that don't impose cropping on their children. It may need to be larger
         // than the display size because fullscreen windows can be shifted offscreen
@@ -2135,6 +2163,9 @@
                     + " to its current displayId=" + mDisplayId);
         }
 
+        // Clean up all pending transitions when stack reparent to another display.
+        stack.forAllAppWindows(AppWindowToken::removeFromPendingTransition);
+
         prevDc.mTaskStackContainers.removeChild(stack);
         mTaskStackContainers.addStackToDisplay(stack, onTop);
     }
@@ -2294,6 +2325,13 @@
     void removeImmediately() {
         mRemovingDisplay = true;
         try {
+            // Clear all transitions & screen frozen states when removing display.
+            mOpeningApps.clear();
+            mClosingApps.clear();
+            mUnknownAppVisibilityController.clear();
+            mAppTransition.removeAppTransitionTimeoutCallbacks();
+            handleAnimatingStoppedAndTransition();
+            mService.stopFreezingDisplayLocked();
             super.removeImmediately();
             if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
             if (mPointerEventDispatcher != null && mTapDetector != null) {
@@ -2514,6 +2552,7 @@
             screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
         }
         mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
+        mAppTransition.writeToProto(proto, APP_TRANSITION);
         proto.write(SURFACE_SIZE, mSurfaceSize);
         if (mFocusedApp != null) {
             mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
@@ -2998,11 +3037,10 @@
                 }
 
                 if (highestTarget != null) {
-                    final AppTransition appTransition = mService.mAppTransition;
-                    if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, appTransition + " " + highestTarget
+                    if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, mAppTransition + " " + highestTarget
                             + " animating=" + highestTarget.isAnimating());
 
-                    if (appTransition.isTransitionSet()) {
+                    if (mAppTransition.isTransitionSet()) {
                         // If we are currently setting up for an animation, hold everything until we
                         // can find out what will happen.
                         setInputMethodTarget(highestTarget, true);
@@ -3093,6 +3131,18 @@
                 pw.println();
             }
         }
+
+        if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty()) {
+            pw.println();
+            if (mOpeningApps.size() > 0) {
+                pw.print("  mOpeningApps="); pw.println(mOpeningApps);
+            }
+            if (mClosingApps.size() > 0) {
+                pw.print("  mClosingApps="); pw.println(mClosingApps);
+            }
+        }
+
+        mUnknownAppVisibilityController.dump(pw, "  ");
     }
 
     void dumpWindowAnimators(PrintWriter pw, String subPrefix) {
@@ -3989,7 +4039,7 @@
                 final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
                 for (int j = appTokens.size() - 1; j >= 0; --j) {
                     final AppWindowToken token = appTokens.get(j);
-                    if (!token.hasVisible && !mService.mClosingApps.contains(token)
+                    if (!token.hasVisible && !mClosingApps.contains(token)
                             && (!token.mIsExiting || token.isEmpty())) {
                         // Make sure there is no animation running on this token, so any windows
                         // associated with it will be removed as soon as their animations are
@@ -4287,8 +4337,8 @@
             // Only allow force setting the orientation when all unknown visibilities have been
             // resolved, as otherwise we just may be starting another occluding activity.
             final boolean isUnoccluding =
-                    mService.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
-                            && mService.mUnknownAppVisibilityController.allResolved();
+                    mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
+                            && mUnknownAppVisibilityController.allResolved();
             if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
                 return mLastKeyguardForcedOrientation;
             }
@@ -4528,4 +4578,56 @@
             mPointerEventDispatcher.unregisterInputEventListener(listener);
         }
     }
+
+    void prepareAppTransition(@WindowManager.TransitionType int transit,
+            boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags,
+            boolean forceOverride) {
+        final boolean prepared = mAppTransition.prepareAppTransitionLocked(
+                transit, alwaysKeepCurrent, flags, forceOverride);
+        if (prepared && okToAnimate()) {
+            mSkipAppTransitionAnimation = false;
+        }
+    }
+
+    void executeAppTransition() {
+        if (mAppTransition.isTransitionSet()) {
+            if (DEBUG_APP_TRANSITIONS) {
+                Slog.w(TAG_WM, "Execute app transition: " + mAppTransition + ", displayId: "
+                        + mDisplayId + " Callers=" + Debug.getCallers(5));
+            }
+            mAppTransition.setReady();
+            mService.mWindowPlacerLocked.requestTraversal();
+        }
+    }
+
+    /**
+     * Update pendingLayoutChanges after app transition has finished.
+     */
+    void handleAnimatingStoppedAndTransition() {
+        int changes = 0;
+
+        mAppTransition.setIdle();
+
+        for (int i = mNoAnimationNotifyOnTransitionFinished.size() - 1; i >= 0; i--) {
+            final IBinder token = mNoAnimationNotifyOnTransitionFinished.get(i);
+            mAppTransition.notifyAppTransitionFinishedLocked(token);
+        }
+        mNoAnimationNotifyOnTransitionFinished.clear();
+
+        mWallpaperController.hideDeferredWallpapersIfNeeded();
+
+        onAppTransitionDone();
+
+        changes |= FINISH_LAYOUT_REDO_LAYOUT;
+        if (DEBUG_WALLPAPER_LIGHT) {
+            Slog.v(TAG_WM, "Wallpaper layer changed: assigning layers + relayout");
+        }
+        computeImeTarget(true /* updateImeTarget */);
+        mService.mRoot.mWallpaperMayChange = true;
+        // Since the window list has been rebuilt, focus might have to be recomputed since the
+        // actual order of windows might have changed again.
+        mService.mFocusMayChange = true;
+
+        pendingLayoutChanges |= changes;
+    }
 }