Remove AppWindowContainerController and AppWindowContainerListener (39/n)

AppWindowToken and ActivityRecord will hold each other as a member and
call directly into the other. Unnecessary locks are removed.

All AppWindowContainerControllerTests moved to AppWindowTokenTests.
Three of the tests needs other works of the hierarchy which can be added
back later. Commented out with a TODO.

Bug: 80414790
Test: go/wm-smoke
Test: atest ActivityRecordTests
Test: atest AppWindowTokenTests
Test: atest FrameworksServicesTests:AppTransitionTests
Test: atest ActivityStackSupervisorTests
Test: atest ActivityStackTests
Change-Id: Ifa1f692f5c54dbffc3758bd26de124e7d055e0ab
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5e92b9e..6a68fab 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -18,7 +18,17 @@
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
 import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -63,6 +73,7 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
 import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
 import static android.content.res.Configuration.EMPTY;
@@ -111,12 +122,18 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService
+        .RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
 import static com.android.server.wm.TaskPersister.DEBUG;
 import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -147,6 +164,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Build;
@@ -167,11 +185,14 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IApplicationToken;
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.util.XmlUtils;
@@ -200,7 +221,7 @@
 /**
  * An entry in the history stack, representing an activity.
  */
-final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
+final class ActivityRecord extends ConfigurationContainer {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
@@ -225,7 +246,9 @@
 
     final ActivityTaskManagerService service; // owner
     final IApplicationToken.Stub appToken; // window manager token
-    AppWindowContainerController mWindowContainerController;
+    // TODO: Remove after unification
+    AppWindowToken mAppWindowToken;
+
     final ActivityInfo info; // all about me
     // TODO: This is duplicated state already contained in info.applicationInfo - remove
     ApplicationInfo appInfo; // information about activity's app
@@ -769,10 +792,16 @@
     }
 
     /**
-     * See {@link AppWindowContainerController#setWillCloseOrEnterPip(boolean)}
+     * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
+     * This information helps AWT know that the app is in the process of pausing before it gets the
+     * signal on the WM side.
      */
     void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
-        getWindowContainerController().setWillCloseOrEnterPip(willCloseOrEnterPip);
+        if (mAppWindowToken == null) {
+            return;
+        }
+
+        mAppWindowToken.setWillCloseOrEnterPip(willCloseOrEnterPip);
     }
 
     static class Token extends IApplicationToken.Stub {
@@ -991,13 +1020,9 @@
         return hasProcess() && app.hasThread();
     }
 
-    AppWindowContainerController getWindowContainerController() {
-        return mWindowContainerController;
-    }
-
-    void createWindowContainer() {
-        if (mWindowContainerController != null) {
-            throw new IllegalArgumentException("Window container=" + mWindowContainerController
+    void createAppWindowToken() {
+        if (mAppWindowToken != null) {
+            throw new IllegalArgumentException("App Window Token=" + mAppWindowToken
                     + " already created for r=" + this);
         }
 
@@ -1010,12 +1035,31 @@
         // Make sure override configuration is up-to-date before using to create window controller.
         updateOverrideConfiguration();
 
-        mWindowContainerController = new AppWindowContainerController(taskController, appToken,
-                realActivity, this, Integer.MAX_VALUE /* add on top */, info.screenOrientation,
-                fullscreen, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
-                task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
-                appInfo.targetSdkVersion, mRotationAnimationHint,
-                ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
+        // TODO: remove after unification
+        mAppWindowToken = service.mWindowManager.mRoot.getAppWindowToken(appToken.asBinder());
+        if (mAppWindowToken != null) {
+            // TODO: Should this throw an exception instead?
+            Slog.w(TAG, "Attempted to add existing app token: " + appToken);
+        } else {
+            final Task container = taskController.mContainer;
+            if (container == null) {
+                throw new IllegalArgumentException("AppWindowContainerController: invalid "
+                        + " controller=" + taskController);
+            }
+            mAppWindowToken = createAppWindow(service.mWindowManager, appToken,
+                    task.voiceSession != null, container.getDisplayContent(),
+                    ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this)
+                            * 1000000L, fullscreen,
+                    (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, appInfo.targetSdkVersion,
+                    info.screenOrientation, mRotationAnimationHint, info.configChanges,
+                    mLaunchTaskBehind, isAlwaysFocusable());
+            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) {
+                Slog.v(TAG, "addAppToken: "
+                        + mAppWindowToken + " controller=" + taskController + " at "
+                        + Integer.MAX_VALUE);
+            }
+            container.addChild(mAppWindowToken, Integer.MAX_VALUE /* add on top */);
+        }
 
         task.addActivityToTop(this);
 
@@ -1026,17 +1070,49 @@
         mLastReportedPictureInPictureMode = inPinnedWindowingMode();
     }
 
+    boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
+            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+        if (DEBUG_STARTING_WINDOW) {
+            Slog.v(TAG, "setAppStartingWindow: token=" + appToken
+                    + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
+                    + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
+                    + " allowTaskSnapshot=" + allowTaskSnapshot);
+        }
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + appToken);
+            return false;
+        }
+        return mAppWindowToken.addStartingWindow(pkg, theme, compatInfo, nonLocalizedLabel,
+                labelRes, icon, logo, windowFlags, transferFrom, newTask, taskSwitch,
+                processRunning, allowTaskSnapshot, activityCreated, fromRecents);
+    }
+
+    // TODO: Remove after unification
+    @VisibleForTesting
+    AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
+            boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
+            boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
+            int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
+            boolean alwaysFocusable) {
+        return new AppWindowToken(service, token, realActivity, voiceInteraction, dc,
+                inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
+                rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
+                this);
+    }
+
     void removeWindowContainer() {
-        // Do not try to remove a window container if we have already removed it.
-        if (mWindowContainerController == null) {
+        final DisplayContent dc = service.mWindowManager.mRoot.getDisplayContent(
+                getDisplayId());
+        if (dc == null) {
+            Slog.w(TAG, "removeWindowContainer: Attempted to remove token: "
+                    + appToken + " from non-existing displayId=" + getDisplayId());
             return;
         }
-
         // Resume key dispatching if it is currently paused before we remove the container.
         resumeKeyDispatchingLocked();
-
-        mWindowContainerController.removeContainer(getDisplayId());
-        mWindowContainerController = null;
+        dc.removeAppToken(appToken.asBinder());
     }
 
     /**
@@ -1044,6 +1120,10 @@
      * should ensure that the {@param newTask} is not already the parent of this activity.
      */
     void reparent(TaskRecord newTask, int position, String reason) {
+        if (mAppWindowToken == null) {
+            Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken);
+            return;
+        }
         final TaskRecord prevTask = task;
         if (prevTask == newTask) {
             throw new IllegalArgumentException(reason + ": task=" + newTask
@@ -1059,8 +1139,7 @@
                     + " r=" + this + " (" + prevTask.getStackId() + ")");
         }
 
-        // Must reparent first in window manager
-        mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
+        mAppWindowToken.reparent(newTask.getWindowContainerController(), position);
 
         // Reparenting prevents informing the parent stack of activity removal in the case that
         // the new stack has the same parent. we must manually signal here if this is not the case.
@@ -1490,7 +1569,7 @@
     void applyOptionsLocked() {
         if (pendingOptions != null
                 && pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) {
-            mWindowContainerController.applyOptionsLocked(pendingOptions, intent);
+            applyOptionsLocked(pendingOptions, intent);
             if (task == null) {
                 clearOptionsLocked(false /* withAbort */);
             } else {
@@ -1500,6 +1579,104 @@
         }
     }
 
+    /**
+     * Apply override app transition base on options & animation type.
+     */
+    void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
+        final int animationType = pendingOptions.getAnimationType();
+        final DisplayContent displayContent = mAppWindowToken.getDisplayContent();
+        switch (animationType) {
+            case ANIM_CUSTOM:
+                displayContent.mAppTransition.overridePendingAppTransition(
+                        pendingOptions.getPackageName(),
+                        pendingOptions.getCustomEnterResId(),
+                        pendingOptions.getCustomExitResId(),
+                        pendingOptions.getOnAnimationStartListener());
+                break;
+            case ANIM_CLIP_REVEAL:
+                displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
+                        pendingOptions.getStartX(), pendingOptions.getStartY(),
+                        pendingOptions.getWidth(), pendingOptions.getHeight());
+                if (intent.getSourceBounds() == null) {
+                    intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                            pendingOptions.getStartY(),
+                            pendingOptions.getStartX() + pendingOptions.getWidth(),
+                            pendingOptions.getStartY() + pendingOptions.getHeight()));
+                }
+                break;
+            case ANIM_SCALE_UP:
+                displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
+                        pendingOptions.getStartX(), pendingOptions.getStartY(),
+                        pendingOptions.getWidth(), pendingOptions.getHeight());
+                if (intent.getSourceBounds() == null) {
+                    intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                            pendingOptions.getStartY(),
+                            pendingOptions.getStartX() + pendingOptions.getWidth(),
+                            pendingOptions.getStartY() + pendingOptions.getHeight()));
+                }
+                break;
+            case ANIM_THUMBNAIL_SCALE_UP:
+            case ANIM_THUMBNAIL_SCALE_DOWN:
+                final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
+                final GraphicBuffer buffer = pendingOptions.getThumbnail();
+                displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
+                        pendingOptions.getStartX(), pendingOptions.getStartY(),
+                        pendingOptions.getOnAnimationStartListener(),
+                        scaleUp);
+                if (intent.getSourceBounds() == null && buffer != null) {
+                    intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                            pendingOptions.getStartY(),
+                            pendingOptions.getStartX() + buffer.getWidth(),
+                            pendingOptions.getStartY() + buffer.getHeight()));
+                }
+                break;
+            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
+            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
+                final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
+                final IAppTransitionAnimationSpecsFuture specsFuture =
+                        pendingOptions.getSpecsFuture();
+                if (specsFuture != null) {
+                    // TODO(multidisplay): Shouldn't be really used anymore from next CL.
+                    displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
+                            specsFuture, pendingOptions.getOnAnimationStartListener(),
+                            animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
+                } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
+                        && specs != null) {
+                    displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
+                            specs, pendingOptions.getOnAnimationStartListener(),
+                            pendingOptions.getAnimationFinishedListener(), false);
+                } else {
+                    displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
+                            pendingOptions.getThumbnail(),
+                            pendingOptions.getStartX(), pendingOptions.getStartY(),
+                            pendingOptions.getWidth(), pendingOptions.getHeight(),
+                            pendingOptions.getOnAnimationStartListener(),
+                            (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
+                    if (intent.getSourceBounds() == null) {
+                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                                pendingOptions.getStartY(),
+                                pendingOptions.getStartX() + pendingOptions.getWidth(),
+                                pendingOptions.getStartY() + pendingOptions.getHeight()));
+                    }
+                }
+                break;
+            case ANIM_OPEN_CROSS_PROFILE_APPS:
+                displayContent.mAppTransition
+                        .overridePendingAppTransitionStartCrossProfileApps();
+                break;
+            case ANIM_REMOTE_ANIMATION:
+                // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL.
+                displayContent.mAppTransition.overridePendingAppTransitionRemote(
+                        pendingOptions.getRemoteAnimationAdapter());
+                break;
+            case ANIM_NONE:
+                break;
+            default:
+                Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
+                break;
+        }
+    }
+
     ActivityOptions getOptionsForTargetActivityLocked() {
         return pendingOptions != null ? pendingOptions.forTargetActivity() : null;
     }
@@ -1532,8 +1709,11 @@
         if (!keysPaused) {
             keysPaused = true;
 
-            if (mWindowContainerController != null) {
-                mWindowContainerController.pauseKeyDispatching();
+            // TODO: remove the check after unification with AppWindowToken. The DC check is not
+            // needed after no mock mAppWindowToken in tests.
+            if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
+                mAppWindowToken.getDisplayContent().getInputMonitor().pauseDispatchingLw(
+                        mAppWindowToken);
             }
         }
     }
@@ -1542,8 +1722,11 @@
         if (keysPaused) {
             keysPaused = false;
 
-            if (mWindowContainerController != null) {
-                mWindowContainerController.resumeKeyDispatching();
+            // TODO: remove the check after unification with AppWindowToken. The DC check is not
+            // needed after no mock mAppWindowToken in tests.
+            if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
+                mAppWindowToken.getDisplayContent().getInputMonitor().resumeDispatchingLw(
+                        mAppWindowToken);
             }
         }
     }
@@ -1565,11 +1748,16 @@
     }
 
     void setVisibility(boolean visible) {
-        mWindowContainerController.setVisibility(visible, mDeferHidingClient);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
+                    + appToken);
+            return;
+        }
+        mAppWindowToken.setVisibility(visible, mDeferHidingClient);
         mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
     }
 
-    // TODO: Look into merging with #setVisibility()
+    // TODO: Look into merging with #commitVisibility()
     void setVisible(boolean newVisible) {
         visible = newVisible;
         mDeferHidingClient = !visible && mDeferHidingClient;
@@ -1599,7 +1787,12 @@
         // an indication that the Surface will eventually be destroyed.
         // This however isn't necessarily true if we are going to sleep.
         if (state == STOPPING && !isSleeping()) {
-            mWindowContainerController.notifyAppStopping();
+            if (mAppWindowToken == null) {
+                Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
+                        + appToken);
+                return;
+            }
+            mAppWindowToken.detachChildren();
         }
     }
 
@@ -1637,7 +1830,12 @@
     }
 
     void notifyAppResumed(boolean wasStopped) {
-        mWindowContainerController.notifyAppResumed(wasStopped);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: "
+                    + appToken);
+            return;
+        }
+        mAppWindowToken.notifyAppResumed(wasStopped);
     }
 
     void notifyUnknownVisibilityLaunched() {
@@ -1645,7 +1843,10 @@
         // No display activities never add a window, so there is no point in waiting them for
         // relayout.
         if (!noDisplay) {
-            mWindowContainerController.notifyUnknownVisibilityLaunched();
+            if (mAppWindowToken != null) {
+                mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController
+                        .notifyLaunched(mAppWindowToken);
+            }
         }
     }
 
@@ -1857,7 +2058,9 @@
             stopped = true;
             setState(STOPPED, "activityStoppedLocked");
 
-            mWindowContainerController.notifyAppStopped();
+            if (mAppWindowToken != null) {
+                mAppWindowToken.notifyAppStopped();
+            }
 
             if (finishing) {
                 clearOptionsLocked();
@@ -1918,14 +2121,33 @@
 
     public void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
         if (mayFreezeScreenLocked(app)) {
-            mWindowContainerController.startFreezingScreen(configChanges);
+            if (mAppWindowToken == null) {
+                Slog.w(TAG_WM,
+                        "Attempted to freeze screen with non-existing app token: " + appToken);
+                return;
+            }
+
+            if (configChanges == 0 && mAppWindowToken.okToDisplay()) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + appToken);
+                return;
+            }
+
+            mAppWindowToken.startFreezingScreen();
         }
     }
 
     public void stopFreezingScreenLocked(boolean force) {
         if (force || frozenBeforeDestroy) {
             frozenBeforeDestroy = false;
-            mWindowContainerController.stopFreezingScreen(force);
+            if (mAppWindowToken == null) {
+                return;
+            }
+            if (DEBUG_ORIENTATION) {
+                Slog.v(TAG_WM, "Clear freezing of " + appToken + ": hidden="
+                        + mAppWindowToken.isHidden() + " freezing="
+                        + mAppWindowToken.isFreezingScreen());
+            }
+            mAppWindowToken.stopFreezingScreen(true, force);
         }
     }
 
@@ -1937,7 +2159,10 @@
                     info.windowsFullyDrawnDelayMs);
         }
     }
-    @Override
+
+    /**
+     * Called when the starting window for this container is drawn.
+     */
     public void onStartingWindowDrawn(long timestamp) {
         synchronized (service.mGlobalLock) {
             mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
@@ -1945,7 +2170,7 @@
         }
     }
 
-    @Override
+    /** Called when the windows associated app window container are drawn. */
     public void onWindowsDrawn(boolean drawn, long timestamp) {
         synchronized (service.mGlobalLock) {
             mDrawn = drawn;
@@ -1965,7 +2190,7 @@
         }
     }
 
-    @Override
+    /** Called when the windows associated app window container are visible. */
     public void onWindowsVisible() {
         synchronized (service.mGlobalLock) {
             mStackSupervisor.reportActivityVisibleLocked(this);
@@ -1999,7 +2224,7 @@
         }
     }
 
-    @Override
+    /** Called when the windows associated app window container are no longer visible. */
     public void onWindowsGone() {
         synchronized (service.mGlobalLock) {
             if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
@@ -2007,7 +2232,14 @@
         }
     }
 
-    @Override
+    /**
+     * Called when the key dispatching to a window associated with the app window container
+     * timed-out.
+     *
+     * @param reason The reason for the key dispatching time out.
+     * @param windowPid The pid of the window key dispatching timed out on.
+     * @return True if input dispatching should be aborted.
+     */
     public boolean keyDispatchingTimedOut(String reason, int windowPid) {
         ActivityRecord anrActivity;
         WindowProcessController anrApp;
@@ -2183,7 +2415,7 @@
 
     void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
             boolean fromRecents) {
-        if (mWindowContainerController == null) {
+        if (mAppWindowToken == null) {
             return;
         }
         if (mTaskOverlay) {
@@ -2198,7 +2430,7 @@
 
         final CompatibilityInfo compatInfo =
                 service.compatibilityInfoForPackageLocked(info.applicationInfo);
-        final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
+        final boolean shown = addStartingWindow(packageName, theme,
                 compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                 prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
                 allowTaskSnapshot(),
@@ -2213,12 +2445,12 @@
         if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) {
             if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
             mStartingWindowState = STARTING_WINDOW_REMOVED;
-            mWindowContainerController.removeStartingWindow();
+            mAppWindowToken.removeStartingWindow();
         }
     }
 
     int getRequestedOrientation() {
-        return mWindowContainerController.getOrientation();
+        return getOrientation();
     }
 
     void setRequestedOrientation(int requestedOrientation) {
@@ -2226,7 +2458,7 @@
         final Configuration displayConfig =
                 mStackSupervisor.getDisplayOverrideConfiguration(displayId);
 
-        final Configuration config = mWindowContainerController.setOrientation(requestedOrientation,
+        final Configuration config = setOrientation(requestedOrientation,
                 displayId, displayConfig, mayFreezeScreenLocked(app));
         if (config != null) {
             frozenBeforeDestroy = true;
@@ -2239,8 +2471,36 @@
                 task.taskId, requestedOrientation);
     }
 
+    Configuration setOrientation(int requestedOrientation, int displayId,
+            Configuration displayConfig, boolean freezeScreenIfNeeded) {
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM,
+                    "Attempted to set orientation of non-existing app token: " + appToken);
+            return null;
+        }
+
+        mAppWindowToken.setOrientation(requestedOrientation);
+
+        final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
+        return service.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder,
+                displayId);
+    }
+
+    int getOrientation() {
+        if (mAppWindowToken == null) {
+            return SCREEN_ORIENTATION_UNSPECIFIED;
+        }
+
+        return mAppWindowToken.getOrientationIgnoreVisibility();
+    }
+
     void setDisablePreviewScreenshots(boolean disable) {
-        mWindowContainerController.setDisablePreviewScreenshots(disable);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
+                    + " token: " + appToken);
+            return;
+        }
+        mAppWindowToken.setDisablePreviewScreenshots(disable);
     }
 
     /**
@@ -2288,8 +2548,8 @@
 
     /** Returns true if the configuration is compatible with this activity. */
     boolean isConfigurationCompatible(Configuration config) {
-        final int orientation = mWindowContainerController != null
-                ? mWindowContainerController.getOrientation() : info.screenOrientation;
+        final int orientation = mAppWindowToken != null
+                ? getOrientation() : info.screenOrientation;
         if (isFixedOrientationPortrait(orientation)
                 && config.orientation != ORIENTATION_PORTRAIT) {
             return false;
@@ -2918,7 +3178,12 @@
     }
 
     void registerRemoteAnimations(RemoteAnimationDefinition definition) {
-        mWindowContainerController.registerRemoteAnimations(definition);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
+                    + " token: " + appToken);
+            return;
+        }
+        mAppWindowToken.registerRemoteAnimations(definition);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index bd3e43c..df60961 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -3043,7 +3043,7 @@
                     if (!startIt) {
                         if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
                                 + task, new RuntimeException("here").fillInStackTrace());
-                        r.createWindowContainer();
+                        r.createAppWindowToken();
                         ActivityOptions.abort(options);
                         return;
                     }
@@ -3073,9 +3073,10 @@
         // TODO: Need to investigate if it is okay for the controller to already be created by the
         // time we get to this point. I think it is, but need to double check.
         // Use test in b/34179495 to trace the call path.
-        if (r.getWindowContainerController() == null) {
-            r.createWindowContainer();
+        if (r.mAppWindowToken == null) {
+            r.createAppWindowToken();
         }
+
         task.setFrontOfTask();
 
         if (!isHomeOrRecentsStack() || numActivities() > 0) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index c517bd7..4e32184 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -3174,7 +3174,7 @@
                 "Added restored task=" + task + " to stack=" + stack);
         final ArrayList<ActivityRecord> activities = task.mActivities;
         for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-            activities.get(activityNdx).createWindowContainer();
+            activities.get(activityNdx).createAppWindowToken();
         }
         return true;
     }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 32a6f74..bf00ffb 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -305,7 +305,7 @@
             AppWindowToken wtoken = openingApps.valueAt(i);
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
 
-            if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) {
+            if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) {
                 // This token isn't going to be animating. Add it to the list of tokens to
                 // be notified of app transition complete since the notification will not be
                 // sent be the app window animator.
@@ -341,7 +341,7 @@
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
             // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
             //       animating?
-            wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
+            wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction);
             wtoken.updateReportedVisibilityLocked();
             // Force the allDrawn flag, because we want to start
             // this guy's animations regardless of whether it's
@@ -350,9 +350,8 @@
             wtoken.deferClearAllDrawn = false;
             // Ensure that apps that are mid-starting are also scheduled to have their
             // starting windows removed after the animation is complete
-            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
-                    && wtoken.getController() != null) {
-                wtoken.getController().removeStartingWindow();
+            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) {
+                wtoken.removeStartingWindow();
             }
 
             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
deleted file mode 100644
index 7fdea10..0000000
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ /dev/null
@@ -1,912 +0,0 @@
-/*
- * Copyright (C) 2016 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 static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
-import static android.app.ActivityOptions.ANIM_CUSTOM;
-import static android.app.ActivityOptions.ANIM_NONE;
-import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
-import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
-import static android.app.ActivityOptions.ANIM_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_UNSET;
-
-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_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.app.ActivityManager.TaskSnapshot;
-import android.app.ActivityOptions;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.graphics.GraphicBuffer;
-import android.graphics.Rect;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Slog;
-import android.view.AppTransitionAnimationSpec;
-import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.IApplicationToken;
-import android.view.RemoteAnimationDefinition;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.AttributeCache;
-import com.android.server.policy.WindowManagerPolicy.StartingSurface;
-
-/**
- * Controller for the app window token container. This is created by activity manager to link
- * activity records to the app window token container they use in window manager.
- *
- * Test class: {@link AppWindowContainerControllerTests}
- */
-public class AppWindowContainerController
-        extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
-
-    private static final int STARTING_WINDOW_TYPE_NONE = 0;
-    private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
-    private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
-
-    private final IApplicationToken mToken;
-    private final Handler mHandler;
-
-    private final class H extends Handler {
-        public static final int NOTIFY_WINDOWS_DRAWN = 1;
-        public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2;
-        public static final int NOTIFY_WINDOWS_NOTDRAWN = 3;
-
-        public H(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case NOTIFY_WINDOWS_DRAWN:
-                    if (mListener == null) {
-                        return;
-                    }
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
-                            + AppWindowContainerController.this.mToken);
-                    mListener.onWindowsDrawn(true /* drawn */, msg.getWhen());
-                    break;
-                case NOTIFY_STARTING_WINDOW_DRAWN:
-                    if (mListener == null) {
-                        return;
-                    }
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting starting window drawn in "
-                            + AppWindowContainerController.this.mToken);
-                    mListener.onStartingWindowDrawn(msg.getWhen());
-                    break;
-                case NOTIFY_WINDOWS_NOTDRAWN:
-                    if (mListener == null) {
-                        return;
-                    }
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting not drawn in "
-                            + AppWindowContainerController.this.mToken);
-                    mListener.onWindowsDrawn(false /* drawn */, msg.getWhen());
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    private final Runnable mOnWindowsVisible = () -> {
-        if (mListener == null) {
-            return;
-        }
-        if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting visible in "
-                + AppWindowContainerController.this.mToken);
-        mListener.onWindowsVisible();
-    };
-
-    private final Runnable mOnWindowsGone = () -> {
-        if (mListener == null) {
-            return;
-        }
-        if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in "
-                + AppWindowContainerController.this.mToken);
-        mListener.onWindowsGone();
-    };
-
-    private final Runnable mAddStartingWindow = new Runnable() {
-
-        @Override
-        public void run() {
-            final StartingData startingData;
-            final AppWindowToken container;
-
-            synchronized (mGlobalLock) {
-                if (mContainer == null) {
-                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
-                            + " add starting window");
-                    return;
-                }
-
-                // There can only be one adding request, silly caller!
-                mService.mAnimationHandler.removeCallbacks(this);
-
-                startingData = mContainer.startingData;
-                container = mContainer;
-            }
-
-            if (startingData == null) {
-                // Animation has been canceled... do nothing.
-                if (DEBUG_STARTING_WINDOW)
-                    Slog.v(TAG_WM, "startingData was nulled out before handling"
-                            + " mAddStartingWindow: " + mContainer);
-                return;
-            }
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
-                    + AppWindowContainerController.this + ": startingData="
-                    + container.startingData);
-
-            StartingSurface surface = null;
-            try {
-                surface = startingData.createStartingSurface(container);
-            } catch (Exception e) {
-                Slog.w(TAG_WM, "Exception when adding starting window", e);
-            }
-            if (surface != null) {
-                boolean abort = false;
-                synchronized (mGlobalLock) {
-                    // If the window was successfully added, then
-                    // we need to remove it.
-                    if (container.removed || container.startingData == null) {
-                        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                                "Aborted starting " + container
-                                        + ": removed=" + container.removed
-                                        + " startingData=" + container.startingData);
-                        container.startingWindow = null;
-                        container.startingData = null;
-                        abort = true;
-                    } else {
-                        container.startingSurface = surface;
-                    }
-                    if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
-                            "Added starting " + mContainer
-                                    + ": startingWindow="
-                                    + container.startingWindow + " startingView="
-                                    + container.startingSurface);
-                }
-                if (abort) {
-                    surface.remove();
-                }
-            } else if (DEBUG_STARTING_WINDOW) {
-                Slog.v(TAG_WM, "Surface returned was null: " + mContainer);
-            }
-        }
-    };
-
-    public AppWindowContainerController(TaskWindowContainerController taskController,
-            IApplicationToken token, ComponentName activityComponent,
-            AppWindowContainerListener listener, int index, int requestedOrientation,
-            boolean fullscreen, boolean showForAllUsers, int configChanges,
-            boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
-            int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos) {
-        this(taskController, token, activityComponent, listener, index, requestedOrientation,
-                fullscreen, showForAllUsers, configChanges, voiceInteraction, launchTaskBehind,
-                alwaysFocusable, targetSdkVersion, rotationAnimationHint,
-                inputDispatchingTimeoutNanos, WindowManagerService.getInstance());
-    }
-
-    public AppWindowContainerController(TaskWindowContainerController taskController,
-            IApplicationToken token, ComponentName activityComponent,
-            AppWindowContainerListener listener, int index, int requestedOrientation,
-            boolean fullscreen, boolean showForAllUsers, int configChanges,
-            boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
-            int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
-            WindowManagerService service) {
-        super(listener, service);
-        mHandler = new H(service.mH.getLooper());
-        mToken = token;
-        synchronized (mGlobalLock) {
-            AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
-            if (atoken != null) {
-                // TODO: Should this throw an exception instead?
-                Slog.w(TAG_WM, "Attempted to add existing app token: " + mToken);
-                return;
-            }
-
-            final Task task = taskController.mContainer;
-            if (task == null) {
-                throw new IllegalArgumentException("AppWindowContainerController: invalid "
-                        + " controller=" + taskController);
-            }
-
-            atoken = createAppWindow(mService, token, activityComponent, voiceInteraction,
-                    task.getDisplayContent(), inputDispatchingTimeoutNanos, fullscreen,
-                    showForAllUsers, targetSdkVersion, requestedOrientation, rotationAnimationHint,
-                    configChanges, launchTaskBehind, alwaysFocusable, this);
-            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
-                    + " controller=" + taskController + " at " + index);
-            task.addChild(atoken, index);
-        }
-    }
-
-    @VisibleForTesting
-    AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
-            ComponentName component, boolean voiceInteraction, DisplayContent dc,
-            long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
-            int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
-            boolean launchTaskBehind, boolean alwaysFocusable,
-            AppWindowContainerController controller) {
-        return new AppWindowToken(service, token, component, voiceInteraction, dc,
-                inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
-                rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
-                controller);
-    }
-
-    public void removeContainer(int displayId) {
-        synchronized (mGlobalLock) {
-            final DisplayContent dc = mRoot.getDisplayContent(displayId);
-            if (dc == null) {
-                Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: "
-                        + mToken + " from non-existing displayId=" + displayId);
-                return;
-            }
-            dc.removeAppToken(mToken.asBinder());
-            super.removeContainer();
-        }
-    }
-
-    @Override
-    public void removeContainer() {
-        throw new UnsupportedOperationException("Use removeContainer(displayId) instead.");
-    }
-
-    public void reparent(TaskWindowContainerController taskController, int position) {
-        synchronized (mGlobalLock) {
-            if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken
-                    + " to task=" + taskController + " at " + position);
-            if (mContainer == null) {
-                if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM,
-                        "reparent: could not find app token=" + mToken);
-                return;
-            }
-            final Task task = taskController.mContainer;
-            if (task == null) {
-                throw new IllegalArgumentException("reparent: could not find task="
-                        + taskController);
-            }
-            mContainer.reparent(task, position);
-            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-        }
-    }
-
-    public Configuration setOrientation(int requestedOrientation, int displayId,
-            Configuration displayConfig, boolean freezeScreenIfNeeded) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM,
-                        "Attempted to set orientation of non-existing app token: " + mToken);
-                return null;
-            }
-
-            mContainer.setOrientation(requestedOrientation);
-
-            final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null;
-            return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId);
-
-        }
-    }
-
-    public int getOrientation() {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                return SCREEN_ORIENTATION_UNSPECIFIED;
-            }
-
-            return mContainer.getOrientationIgnoreVisibility();
-        }
-    }
-
-    public void setDisablePreviewScreenshots(boolean disable) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
-                        + " token: " + mToken);
-                return;
-            }
-            mContainer.setDisablePreviewScreenshots(disable);
-        }
-    }
-
-    public void setVisibility(boolean visible, boolean deferHidingClient) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
-                        + mToken);
-                return;
-            }
-
-            final AppWindowToken wtoken = mContainer;
-            final AppTransition appTransition = mContainer.getDisplayContent().mAppTransition;
-
-            // Don't set visibility to false if we were already not visible. This prevents WM from
-            // adding the app to the closing app list which doesn't make sense for something that is
-            // already not visible. However, set visibility to true even if we are already visible.
-            // This makes sure the app is added to the opening apps list so that the right
-            // transition can be selected.
-            // TODO: Probably a good idea to separate the concept of opening/closing apps from the
-            // concept of setting visibility...
-            if (!visible && wtoken.hiddenRequested) {
-
-                if (!deferHidingClient && wtoken.mDeferHidingClient) {
-                    // We previously deferred telling the client to hide itself when visibility was
-                    // initially set to false. Now we would like it to hide, so go ahead and set it.
-                    wtoken.mDeferHidingClient = deferHidingClient;
-                    wtoken.setClientHidden(true);
-                }
-                return;
-            }
-
-            if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility("
-                    + mToken + ", visible=" + visible + "): " + appTransition
-                    + " hidden=" + wtoken.isHidden() + " hiddenRequested="
-                    + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
-
-            final DisplayContent displayContent = mContainer.getDisplayContent();
-            displayContent.mOpeningApps.remove(wtoken);
-            displayContent.mClosingApps.remove(wtoken);
-            wtoken.waitingToShow = false;
-            wtoken.hiddenRequested = !visible;
-            wtoken.mDeferHidingClient = deferHidingClient;
-
-            if (!visible) {
-                // If the app is dead while it was visible, we kept its dead window on screen.
-                // Now that the app is going invisible, we can remove it. It will be restarted
-                // if made visible again.
-                wtoken.removeDeadWindows();
-            } else {
-                if (!appTransition.isTransitionSet()
-                        && appTransition.isReady()) {
-                    // Add the app mOpeningApps if transition is unset but ready. This means
-                    // we're doing a screen freeze, and the unfreeze will wait for all opening
-                    // apps to be ready.
-                    displayContent.mOpeningApps.add(wtoken);
-                }
-                wtoken.startingMoved = false;
-                // If the token is currently hidden (should be the common case), or has been
-                // stopped, then we need to set up to wait for its windows to be ready.
-                if (wtoken.isHidden() || wtoken.mAppStopped) {
-                    wtoken.clearAllDrawn();
-
-                    // If the app was already visible, don't reset the waitingToShow state.
-                    if (wtoken.isHidden()) {
-                        wtoken.waitingToShow = true;
-                    }
-                }
-
-                // In the case where we are making an app visible but holding off for a transition,
-                // we still need to tell the client to make its windows visible so they get drawn.
-                // Otherwise, we will wait on performing the transition until all windows have been
-                // drawn, they never will be, and we are sad.
-                wtoken.setClientHidden(false);
-
-                wtoken.requestUpdateWallpaperIfNeeded();
-
-                if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken);
-                wtoken.mAppStopped = false;
-
-                mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
-            }
-
-            // If we are preparing an app transition, then delay changing
-            // the visibility of this token until we execute that transition.
-            if (wtoken.okToAnimate() && appTransition.isTransitionSet()) {
-                wtoken.inPendingTransaction = true;
-                if (visible) {
-                    displayContent.mOpeningApps.add(wtoken);
-                    wtoken.mEnteringAnimation = true;
-                } else {
-                    displayContent.mClosingApps.add(wtoken);
-                    wtoken.mEnteringAnimation = false;
-                }
-                if (appTransition.getAppTransition()
-                        == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
-                    // We're launchingBehind, add the launching activity to mOpeningApps.
-                    final WindowState win = mContainer.getDisplayContent().findFocusedWindow();
-                    if (win != null) {
-                        final AppWindowToken focusedToken = win.mAppToken;
-                        if (focusedToken != null) {
-                            if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
-                                    + " adding " + focusedToken + " to mOpeningApps");
-                            // Force animation to be loaded.
-                            focusedToken.setHidden(true);
-                            displayContent.mOpeningApps.add(focusedToken);
-                        }
-                    }
-                }
-                return;
-            }
-
-            wtoken.setVisibility(null, visible, TRANSIT_UNSET, true, wtoken.mVoiceInteraction);
-            wtoken.updateReportedVisibilityLocked();
-        }
-    }
-
-    /**
-     * Notifies that we launched an app that might be visible or not visible depending on what kind
-     * of Keyguard flags it's going to set on its windows.
-     */
-    public void notifyUnknownVisibilityLaunched() {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.getDisplayContent().mUnknownAppVisibilityController.notifyLaunched(
-                        mContainer);
-            }
-        }
-    }
-
-    public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
-            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
-            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
-            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
-        synchronized (mGlobalLock) {
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken
-                    + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
-                    + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
-                    + " allowTaskSnapshot=" + allowTaskSnapshot);
-
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + mToken);
-                return false;
-            }
-
-            // If the display is frozen, we won't do anything until the actual window is
-            // displayed so there is no reason to put in the starting window.
-            if (!mContainer.okToDisplay()) {
-                return false;
-            }
-
-            if (mContainer.startingData != null) {
-                return false;
-            }
-
-            final WindowState mainWin = mContainer.findMainWindow();
-            if (mainWin != null && mainWin.mWinAnimator.getShown()) {
-                // App already has a visible window...why would you want a starting window?
-                return false;
-            }
-
-            final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
-                    mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
-                    false /* restoreFromDisk */, false /* reducedResolution */);
-            final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
-                    allowTaskSnapshot, activityCreated, fromRecents, snapshot);
-
-            if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
-                return createSnapshot(snapshot);
-            }
-
-            // If this is a translucent window, then don't show a starting window -- the current
-            // effect (a full-screen opaque starting window that fades away to the real contents
-            // when it is ready) does not work for this.
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Checking theme of starting window: 0x"
-                    + Integer.toHexString(theme));
-            if (theme != 0) {
-                AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
-                        com.android.internal.R.styleable.Window, mService.mCurrentUserId);
-                if (ent == null) {
-                    // Whoops!  App doesn't exist. Um. Okay. We'll just pretend like we didn't
-                    // see that.
-                    return false;
-                }
-                final boolean windowIsTranslucent = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowIsTranslucent, false);
-                final boolean windowIsFloating = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowIsFloating, false);
-                final boolean windowShowWallpaper = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowShowWallpaper, false);
-                final boolean windowDisableStarting = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowDisablePreview, false);
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Translucent=" + windowIsTranslucent
-                        + " Floating=" + windowIsFloating
-                        + " ShowWallpaper=" + windowShowWallpaper);
-                if (windowIsTranslucent) {
-                    return false;
-                }
-                if (windowIsFloating || windowDisableStarting) {
-                    return false;
-                }
-                if (windowShowWallpaper) {
-                    if (mContainer.getDisplayContent().mWallpaperController.getWallpaperTarget()
-                            == null) {
-                        // If this theme is requesting a wallpaper, and the wallpaper
-                        // is not currently visible, then this effectively serves as
-                        // an opaque window and our starting window transition animation
-                        // can still work.  We just need to make sure the starting window
-                        // is also showing the wallpaper.
-                        windowFlags |= FLAG_SHOW_WALLPAPER;
-                    } else {
-                        return false;
-                    }
-                }
-            }
-
-            if (mContainer.transferStartingWindow(transferFrom)) {
-                return true;
-            }
-
-            // There is no existing starting window, and we don't want to create a splash screen, so
-            // that's it!
-            if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
-                return false;
-            }
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
-            mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme,
-                    compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
-                    mContainer.getMergedOverrideConfiguration());
-            scheduleAddStartingWindow();
-        }
-        return true;
-    }
-
-    private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
-            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
-            TaskSnapshot snapshot) {
-        if (mContainer.getDisplayContent().mAppTransition.getAppTransition()
-                == TRANSIT_DOCK_TASK_FROM_RECENTS) {
-            // TODO(b/34099271): Remove this statement to add back the starting window and figure
-            // out why it causes flickering, the starting window appears over the thumbnail while
-            // the docked from recents transition occurs
-            return STARTING_WINDOW_TYPE_NONE;
-        } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
-            return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
-        } else if (taskSwitch && allowTaskSnapshot) {
-            return snapshot == null ? STARTING_WINDOW_TYPE_NONE
-                    : snapshotOrientationSameAsTask(snapshot) || fromRecents
-                            ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
-        } else {
-            return STARTING_WINDOW_TYPE_NONE;
-        }
-    }
-
-    void scheduleAddStartingWindow() {
-        // Note: we really want to do sendMessageAtFrontOfQueue() because we
-        // want to process the message ASAP, before any other queued
-        // messages.
-        if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
-            mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
-        }
-    }
-
-    private boolean createSnapshot(TaskSnapshot snapshot) {
-        if (snapshot == null) {
-            return false;
-        }
-
-        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
-        mContainer.startingData = new SnapshotStartingData(mService, snapshot);
-        scheduleAddStartingWindow();
-        return true;
-    }
-
-    private boolean snapshotOrientationSameAsTask(TaskSnapshot snapshot) {
-        if (snapshot == null) {
-            return false;
-        }
-        return mContainer.getTask().getConfiguration().orientation == snapshot.getOrientation();
-    }
-
-    public void removeStartingWindow() {
-        synchronized (mGlobalLock) {
-            if (mContainer.startingWindow == null) {
-                if (mContainer.startingData != null) {
-                    // Starting window has not been added yet, but it is scheduled to be added.
-                    // Go ahead and cancel the request.
-                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                            "Clearing startingData for token=" + mContainer);
-                    mContainer.startingData = null;
-                }
-                return;
-            }
-
-            final StartingSurface surface;
-            if (mContainer.startingData != null) {
-                surface = mContainer.startingSurface;
-                mContainer.startingData = null;
-                mContainer.startingSurface = null;
-                mContainer.startingWindow = null;
-                mContainer.startingDisplayed = false;
-                if (surface == null) {
-                    if (DEBUG_STARTING_WINDOW) {
-                        Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
-                                + "remove");
-                    }
-                    return;
-                }
-            } else {
-                if (DEBUG_STARTING_WINDOW) {
-                    Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
-                            + mContainer);
-                }
-                return;
-            }
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Schedule remove starting " + mContainer
-                    + " startingWindow=" + mContainer.startingWindow
-                    + " startingView=" + mContainer.startingSurface
-                    + " Callers=" + Debug.getCallers(5));
-
-            // Use the same thread to remove the window as we used to add it, as otherwise we end up
-            // with things in the view hierarchy being called from different threads.
-            mService.mAnimationHandler.post(() -> {
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface);
-                try {
-                    surface.remove();
-                } catch (Exception e) {
-                    Slog.w(TAG_WM, "Exception when removing starting window", e);
-                }
-            });
-        }
-    }
-
-    public void pauseKeyDispatching() {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.getDisplayContent().getInputMonitor().pauseDispatchingLw(mContainer);
-            }
-        }
-    }
-
-    public void resumeKeyDispatching() {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.getDisplayContent().getInputMonitor().resumeDispatchingLw(mContainer);
-            }
-        }
-    }
-
-    public void notifyAppResumed(boolean wasStopped) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + mToken);
-                return;
-            }
-            mContainer.notifyAppResumed(wasStopped);
-        }
-    }
-
-    public void notifyAppStopping() {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
-                        + mToken);
-                return;
-            }
-            mContainer.detachChildren();
-        }
-    }
-
-    public void notifyAppStopped() {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: "
-                        + mToken);
-                return;
-            }
-            mContainer.notifyAppStopped();
-        }
-    }
-
-    public void startFreezingScreen(int configChanges) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM,
-                        "Attempted to freeze screen with non-existing app token: " + mContainer);
-                return;
-            }
-
-            if (configChanges == 0 && mContainer.okToDisplay()) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
-                return;
-            }
-
-            mContainer.startFreezingScreen();
-        }
-    }
-
-    public void stopFreezingScreen(boolean force) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                return;
-            }
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden="
-                    + mContainer.isHidden() + " freezing=" + mContainer.isFreezingScreen());
-            mContainer.stopFreezingScreen(true, force);
-        }
-    }
-
-    public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
-                        + " token: " + mToken);
-                return;
-            }
-            mContainer.registerRemoteAnimations(definition);
-        }
-    }
-
-    void reportStartingWindowDrawn() {
-        mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN));
-    }
-
-    void reportWindowsDrawn() {
-        mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN));
-    }
-
-    void reportWindowsNotDrawn() {
-        mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_NOTDRAWN));
-    }
-
-    void reportWindowsVisible() {
-        mHandler.post(mOnWindowsVisible);
-    }
-
-    void reportWindowsGone() {
-        mHandler.post(mOnWindowsGone);
-    }
-
-    /** Calls directly into activity manager so window manager lock shouldn't held. */
-    boolean keyDispatchingTimedOut(String reason, int windowPid) {
-        return mListener != null && mListener.keyDispatchingTimedOut(reason, windowPid);
-    }
-
-    /**
-     * Apply override app transition base on options & animation type.
-     */
-    public void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
-        synchronized (mGlobalLock) {
-            final int animationType = pendingOptions.getAnimationType();
-            final DisplayContent displayContent = mContainer.getDisplayContent();
-            switch (animationType) {
-                case ANIM_CUSTOM:
-                    displayContent.mAppTransition.overridePendingAppTransition(
-                            pendingOptions.getPackageName(),
-                            pendingOptions.getCustomEnterResId(),
-                            pendingOptions.getCustomExitResId(),
-                            pendingOptions.getOnAnimationStartListener());
-                    break;
-                case ANIM_CLIP_REVEAL:
-                    displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getWidth(), pendingOptions.getHeight());
-                    if (intent.getSourceBounds() == null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX() + pendingOptions.getWidth(),
-                                pendingOptions.getStartY() + pendingOptions.getHeight()));
-                    }
-                    break;
-                case ANIM_SCALE_UP:
-                    displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getWidth(), pendingOptions.getHeight());
-                    if (intent.getSourceBounds() == null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX() + pendingOptions.getWidth(),
-                                pendingOptions.getStartY() + pendingOptions.getHeight()));
-                    }
-                    break;
-                case ANIM_THUMBNAIL_SCALE_UP:
-                case ANIM_THUMBNAIL_SCALE_DOWN:
-                    final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
-                    final GraphicBuffer buffer = pendingOptions.getThumbnail();
-                    displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getOnAnimationStartListener(),
-                            scaleUp);
-                    if (intent.getSourceBounds() == null && buffer != null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX() + buffer.getWidth(),
-                                pendingOptions.getStartY() + buffer.getHeight()));
-                    }
-                    break;
-                case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
-                case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
-                    final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
-                    final IAppTransitionAnimationSpecsFuture specsFuture =
-                            pendingOptions.getSpecsFuture();
-                    if (specsFuture != null) {
-                        displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
-                                specsFuture, pendingOptions.getOnAnimationStartListener(),
-                                animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
-                    } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
-                            && specs != null) {
-                        displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
-                                specs, pendingOptions.getOnAnimationStartListener(),
-                                pendingOptions.getAnimationFinishedListener(), false);
-                    } else {
-                        displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
-                                pendingOptions.getThumbnail(),
-                                pendingOptions.getStartX(), pendingOptions.getStartY(),
-                                pendingOptions.getWidth(), pendingOptions.getHeight(),
-                                pendingOptions.getOnAnimationStartListener(),
-                                (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
-                        if (intent.getSourceBounds() == null) {
-                            intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                    pendingOptions.getStartY(),
-                                    pendingOptions.getStartX() + pendingOptions.getWidth(),
-                                    pendingOptions.getStartY() + pendingOptions.getHeight()));
-                        }
-                    }
-                    break;
-                case ANIM_OPEN_CROSS_PROFILE_APPS:
-                    displayContent.mAppTransition
-                            .overridePendingAppTransitionStartCrossProfileApps();
-                    break;
-                case ANIM_REMOTE_ANIMATION:
-                    displayContent.mAppTransition.overridePendingAppTransitionRemote(
-                            pendingOptions.getRemoteAnimationAdapter());
-                    break;
-                case ANIM_NONE:
-                    break;
-                default:
-                    Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
-     * This information helps AWT know that the app is in the process of pausing before it gets the
-     * signal on the WM side.
-     */
-    public void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                return;
-            }
-
-            mContainer.setWillCloseOrEnterPip(willCloseOrEnterPip);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "AppWindowContainerController{"
-                + " token=" + mToken
-                + " mContainer=" + mContainer
-                + " mListener=" + mListener
-                + "}";
-    }
-}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
deleted file mode 100644
index ad27669..0000000
--- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2016 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;
-
-/** Interface used by the creator of the controller to listen to changes with the container. */
-public interface AppWindowContainerListener extends WindowContainerListener {
-    /** Called when the windows associated app window container drawn state changes. */
-    void onWindowsDrawn(boolean drawn, long timestamp);
-    /** Called when the windows associated app window container are visible. */
-    void onWindowsVisible();
-    /** Called when the windows associated app window container are no longer visible. */
-    void onWindowsGone();
-
-    /**
-     * Called when the starting window for this container is drawn.
-     */
-    void onStartingWindowDrawn(long timestamp);
-
-    /**
-     * Called when the key dispatching to a window associated with the app window container
-     * timed-out.
-     *
-     * @param reason The reason for the key dispatching time out.
-     * @param windowPid The pid of the window key dispatching timed out on.
-     * @return True if input dispatching should be aborted.
-     */
-    boolean keyDispatchingTimedOut(String reason, int windowPid);
-}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 52c78ce..d3c9c25 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -28,10 +28,12 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 import static android.view.WindowManager.TRANSIT_UNSET;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
 
@@ -81,7 +83,9 @@
 
 import android.annotation.CallSuper;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.content.ComponentName;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.GraphicBuffer;
 import android.graphics.Point;
@@ -90,6 +94,7 @@
 import android.os.Debug;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -106,6 +111,8 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
+import com.android.server.AttributeCache;
+import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.policy.WindowManagerPolicy.StartingSurface;
 import com.android.server.wm.WindowManagerService.H;
 
@@ -121,7 +128,8 @@
  * Version of WindowToken that is specifically for a particular application (or
  * really activity) that is displaying windows.
  */
-class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener {
+class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener,
+        ConfigurationContainerListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowToken" : TAG_WM;
 
     /**
@@ -226,6 +234,9 @@
 
     private Task mLastParent;
 
+    // TODO: Remove after unification
+    ActivityRecord mActivityRecord;
+
     /**
      * See {@link #canTurnScreenOn()}
      */
@@ -273,14 +284,20 @@
     /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */
     boolean mNeedsAnimationBoundsLayer;
 
+    private static final int STARTING_WINDOW_TYPE_NONE = 0;
+    private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
+    private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
+
     AppWindowToken(WindowManagerService service, IApplicationToken token,
             ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
             long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
             int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
             boolean launchTaskBehind, boolean alwaysFocusable,
-            AppWindowContainerController controller) {
+            ActivityRecord activityRecord) {
         this(service, token, activityComponent, voiceInteraction, dc, fullscreen);
-        setController(controller);
+        // TODO: remove after unification
+        mActivityRecord = activityRecord;
+        mActivityRecord.registerConfigurationChangeListener(this);
         mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
         mShowForAllUsers = showForAllUsers;
         mTargetSdk = targetSdk;
@@ -320,9 +337,7 @@
             // it from behind the starting window, so there is no need for it to also be doing its
             // own stuff.
             win.cancelAnimation();
-            if (getController() != null) {
-                getController().removeStartingWindow();
-            }
+            removeStartingWindow();
         }
         updateReportedVisibilityLocked();
     }
@@ -360,16 +375,9 @@
         }
         if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
                 + numInteresting + " visible=" + numVisible);
-        final AppWindowContainerController controller = getController();
         if (nowDrawn != reportedDrawn) {
-            if (nowDrawn) {
-                if (controller != null) {
-                    controller.reportWindowsDrawn();
-                }
-            } else {
-                if (controller != null) {
-                    controller.reportWindowsNotDrawn();
-                }
+            if (mActivityRecord != null) {
+                mActivityRecord.onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis());
             }
             reportedDrawn = nowDrawn;
         }
@@ -377,16 +385,36 @@
             if (DEBUG_VISIBILITY) Slog.v(TAG,
                     "Visibility changed in " + this + ": vis=" + nowVisible);
             reportedVisible = nowVisible;
-            if (controller != null) {
+            if (mActivityRecord != null) {
                 if (nowVisible) {
-                    controller.reportWindowsVisible();
+                    onWindowsVisible();
                 } else {
-                    controller.reportWindowsGone();
+                    onWindowsGone();
                 }
             }
         }
     }
 
+    private void onWindowsGone() {
+        if (mActivityRecord == null) {
+            return;
+        }
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG_WM, "Reporting gone in " + mActivityRecord.appToken);
+        }
+        mActivityRecord.onWindowsGone();
+    }
+
+    private void onWindowsVisible() {
+        if (mActivityRecord == null) {
+            return;
+        }
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG_WM, "Reporting visible in " + mActivityRecord.appToken);
+        }
+        mActivityRecord.onWindowsVisible();
+    }
+
     boolean isClientHidden() {
         return mClientHidden;
     }
@@ -401,7 +429,116 @@
         sendAppVisibilityToClients();
     }
 
-    boolean setVisibility(WindowManager.LayoutParams lp,
+    void setVisibility(boolean visible, boolean deferHidingClient) {
+        final AppTransition appTransition = getDisplayContent().mAppTransition;
+
+        // Don't set visibility to false if we were already not visible. This prevents WM from
+        // adding the app to the closing app list which doesn't make sense for something that is
+        // already not visible. However, set visibility to true even if we are already visible.
+        // This makes sure the app is added to the opening apps list so that the right
+        // transition can be selected.
+        // TODO: Probably a good idea to separate the concept of opening/closing apps from the
+        // concept of setting visibility...
+        if (!visible && hiddenRequested) {
+
+            if (!deferHidingClient && mDeferHidingClient) {
+                // We previously deferred telling the client to hide itself when visibility was
+                // initially set to false. Now we would like it to hide, so go ahead and set it.
+                mDeferHidingClient = deferHidingClient;
+                setClientHidden(true);
+            }
+            return;
+        }
+
+        if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
+            Slog.v(TAG_WM, "setAppVisibility("
+                    + appToken + ", visible=" + visible + "): " + appTransition
+                    + " hidden=" + isHidden() + " hiddenRequested="
+                    + hiddenRequested + " Callers=" + Debug.getCallers(6));
+        }
+
+        final DisplayContent displayContent = getDisplayContent();
+        displayContent.mOpeningApps.remove(this);
+        displayContent.mClosingApps.remove(this);
+        waitingToShow = false;
+        hiddenRequested = !visible;
+        mDeferHidingClient = deferHidingClient;
+
+        if (!visible) {
+            // If the app is dead while it was visible, we kept its dead window on screen.
+            // Now that the app is going invisible, we can remove it. It will be restarted
+            // if made visible again.
+            removeDeadWindows();
+        } else {
+            if (!appTransition.isTransitionSet()
+                    && appTransition.isReady()) {
+                // Add the app mOpeningApps if transition is unset but ready. This means
+                // we're doing a screen freeze, and the unfreeze will wait for all opening
+                // apps to be ready.
+                displayContent.mOpeningApps.add(this);
+            }
+            startingMoved = false;
+            // If the token is currently hidden (should be the common case), or has been
+            // stopped, then we need to set up to wait for its windows to be ready.
+            if (isHidden() || mAppStopped) {
+                clearAllDrawn();
+
+                // If the app was already visible, don't reset the waitingToShow state.
+                if (isHidden()) {
+                    waitingToShow = true;
+                }
+            }
+
+            // In the case where we are making an app visible but holding off for a transition,
+            // we still need to tell the client to make its windows visible so they get drawn.
+            // Otherwise, we will wait on performing the transition until all windows have been
+            // drawn, they never will be, and we are sad.
+            setClientHidden(false);
+
+            requestUpdateWallpaperIfNeeded();
+
+            if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + this);
+            mAppStopped = false;
+
+            transferStartingWindowFromHiddenAboveTokenIfNeeded();
+        }
+
+        // If we are preparing an app transition, then delay changing
+        // the visibility of this token until we execute that transition.
+        if (okToAnimate() && appTransition.isTransitionSet()) {
+            inPendingTransaction = true;
+            if (visible) {
+                displayContent.mOpeningApps.add(this);
+                mEnteringAnimation = true;
+            } else {
+                displayContent.mClosingApps.add(this);
+                mEnteringAnimation = false;
+            }
+            if (appTransition.getAppTransition()
+                    == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
+                // We're launchingBehind, add the launching activity to mOpeningApps.
+                final WindowState win = getDisplayContent().findFocusedWindow();
+                if (win != null) {
+                    final AppWindowToken focusedToken = win.mAppToken;
+                    if (focusedToken != null) {
+                        if (DEBUG_APP_TRANSITIONS) {
+                            Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
+                                    + " adding " + focusedToken + " to mOpeningApps");
+                        }
+                        // Force animation to be loaded.
+                        focusedToken.setHidden(true);
+                        displayContent.mOpeningApps.add(focusedToken);
+                    }
+                }
+            }
+            return;
+        }
+
+        commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction);
+        updateReportedVisibilityLocked();
+    }
+
+    boolean commitVisibility(WindowManager.LayoutParams lp,
             boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
 
         boolean delayed = false;
@@ -461,8 +598,10 @@
                 forAllWindows(mService::makeWindowFreezingScreenIfNeededLocked, true);
             }
 
-            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setVisibility: " + this
-                    + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
+            if (DEBUG_APP_TRANSITIONS) {
+                Slog.v(TAG_WM, "commitVisibility: " + this
+                        + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
+            }
 
             if (changed) {
                 getDisplayContent().getInputMonitor().setUpdateInputWindowsNeededLw();
@@ -499,10 +638,9 @@
                 mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
             }
 
-            // If we're becoming visible, immediately change client visibility as well although it
-            // usually gets changed in AppWindowContainerController.setVisibility already. However,
-            // there seem to be some edge cases where we change our visibility but client visibility
-            // never gets updated.
+            // If we're becoming visible, immediately change client visibility as well. there seem
+            // to be some edge cases where we change our visibility but client visibility never gets
+            // updated.
             // If we're becoming invisible, update the client visibility if we are not running an
             // animation. Otherwise, we'll update client visibility in onAnimationFinished.
             if (visible || !isReallyAnimating()) {
@@ -596,11 +734,6 @@
         return getWindowConfiguration().canReceiveKeys() || mAlwaysFocusable;
     }
 
-    AppWindowContainerController getController() {
-        final WindowContainerController controller = super.getController();
-        return controller != null ? (AppWindowContainerController) controller : null;
-    }
-
     @Override
     boolean isVisible() {
         // If the app token isn't hidden then it is considered visible and there is no need to check
@@ -637,7 +770,7 @@
 
         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this);
 
-        boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
+        boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
 
         getDisplayContent().mOpeningApps.remove(this);
         getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
@@ -656,8 +789,8 @@
         if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
                 + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
 
-        if (startingData != null && getController() != null) {
-            getController().removeStartingWindow();
+        if (startingData != null) {
+            removeStartingWindow();
         }
 
         // If this window was animating, then we need to ensure that the app transition notifies
@@ -768,9 +901,7 @@
         mAppStopped = true;
         destroySurfaces();
         // Remove any starting window that was added for this app if they are still around.
-        if (getController() != null) {
-            getController().removeStartingWindow();
-        }
+        removeStartingWindow();
     }
 
     void clearAllDrawn() {
@@ -826,9 +957,7 @@
         // TODO: Something smells about the code below...Is there a better way?
         if (startingWindow == win) {
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
-            if (getController() != null) {
-                getController().removeStartingWindow();
-            }
+            removeStartingWindow();
         } else if (mChildren.size() == 0) {
             // If this is the last window and we had requested a starting transition window,
             // well there is no point now.
@@ -845,9 +974,7 @@
             // we need to get rid of the starting transition.
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Last window, removing starting window "
                     + win);
-            if (getController() != null) {
-                getController().removeStartingWindow();
-            }
+            removeStartingWindow();
         }
     }
 
@@ -1021,6 +1148,10 @@
 
     @Override
     void removeChild(WindowState child) {
+        if (!mChildren.contains(child)) {
+            // This can be true when testing.
+            return;
+        }
         super.removeChild(child);
         checkKeyguardFlagsChanged();
         updateLetterboxSurface(child);
@@ -1042,6 +1173,20 @@
         }
     }
 
+    void reparent(TaskWindowContainerController taskController, int position) {
+        if (DEBUG_ADD_REMOVE) {
+            Slog.i(TAG_WM, "reparent: moving app token=" + this
+                    + " to task=" + taskController + " at " + position);
+        }
+        final Task task = taskController.mContainer;
+        if (task == null) {
+            throw new IllegalArgumentException("reparent: could not find task="
+                    + taskController);
+        }
+        reparent(task, position);
+        getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+    }
+
     void reparent(Task task, int position) {
         final Task currentTask = getTask();
         if (task == currentTask) {
@@ -1300,9 +1445,7 @@
             startingData = fromToken.startingData;
             fromToken.startingData = null;
             fromToken.startingMoved = true;
-            if (getController() != null) {
-                getController().scheduleAddStartingWindow();
-            }
+            scheduleAddStartingWindow();
             return true;
         }
 
@@ -1471,6 +1614,10 @@
         }
     }
 
+    boolean keyDispatchingTimedOut(String reason, int windowPid) {
+        return mActivityRecord != null && mActivityRecord.keyDispatchingTimedOut(reason, windowPid);
+    }
+
     /**
      * Updated this app token tracking states for interesting and drawn windows based on the window.
      *
@@ -1533,8 +1680,8 @@
                     }
                 }
             } else if (w.isDrawnLw()) {
-                if (getController() != null) {
-                    getController().reportStartingWindowDrawn();
+                if (mActivityRecord != null) {
+                    mActivityRecord.onStartingWindowDrawn(SystemClock.uptimeMillis());
                 }
                 startingDisplayed = true;
             }
@@ -1601,6 +1748,266 @@
         return this;
     }
 
+    boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
+            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+        // If the display is frozen, we won't do anything until the actual window is
+        // displayed so there is no reason to put in the starting window.
+        if (!okToDisplay()) {
+            return false;
+        }
+
+        if (startingData != null) {
+            return false;
+        }
+
+        final WindowState mainWin = findMainWindow();
+        if (mainWin != null && mainWin.mWinAnimator.getShown()) {
+            // App already has a visible window...why would you want a starting window?
+            return false;
+        }
+
+        final ActivityManager.TaskSnapshot snapshot =
+                mService.mTaskSnapshotController.getSnapshot(
+                        getTask().mTaskId, getTask().mUserId,
+                        false /* restoreFromDisk */, false /* reducedResolution */);
+        final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
+                allowTaskSnapshot, activityCreated, fromRecents, snapshot);
+
+        if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
+            return createSnapshot(snapshot);
+        }
+
+        // If this is a translucent window, then don't show a starting window -- the current
+        // effect (a full-screen opaque starting window that fades away to the real contents
+        // when it is ready) does not work for this.
+        if (DEBUG_STARTING_WINDOW) {
+            Slog.v(TAG, "Checking theme of starting window: 0x" + Integer.toHexString(theme));
+        }
+        if (theme != 0) {
+            AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
+                    com.android.internal.R.styleable.Window,
+                    mService.mCurrentUserId);
+            if (ent == null) {
+                // Whoops!  App doesn't exist. Um. Okay. We'll just pretend like we didn't
+                // see that.
+                return false;
+            }
+            final boolean windowIsTranslucent = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowIsTranslucent, false);
+            final boolean windowIsFloating = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowIsFloating, false);
+            final boolean windowShowWallpaper = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowShowWallpaper, false);
+            final boolean windowDisableStarting = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowDisablePreview, false);
+            if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG, "Translucent=" + windowIsTranslucent
+                        + " Floating=" + windowIsFloating
+                        + " ShowWallpaper=" + windowShowWallpaper);
+            }
+            if (windowIsTranslucent) {
+                return false;
+            }
+            if (windowIsFloating || windowDisableStarting) {
+                return false;
+            }
+            if (windowShowWallpaper) {
+                if (getDisplayContent().mWallpaperController
+                        .getWallpaperTarget() == null) {
+                    // If this theme is requesting a wallpaper, and the wallpaper
+                    // is not currently visible, then this effectively serves as
+                    // an opaque window and our starting window transition animation
+                    // can still work.  We just need to make sure the starting window
+                    // is also showing the wallpaper.
+                    windowFlags |= FLAG_SHOW_WALLPAPER;
+                } else {
+                    return false;
+                }
+            }
+
+            if (transferStartingWindow(transferFrom)) {
+                return true;
+            }
+
+            // There is no existing starting window, and we don't want to create a splash screen, so
+            // that's it!
+            if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+                return false;
+            }
+
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
+            startingData = new SplashScreenStartingData(mService, pkg,
+                    theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+                    getMergedOverrideConfiguration());
+            scheduleAddStartingWindow();
+        }
+        return true;
+    }
+
+
+    private boolean createSnapshot(ActivityManager.TaskSnapshot snapshot) {
+        if (snapshot == null) {
+            return false;
+        }
+
+        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
+        startingData = new SnapshotStartingData(mService, snapshot);
+        scheduleAddStartingWindow();
+        return true;
+    }
+
+    void scheduleAddStartingWindow() {
+        // Note: we really want to do sendMessageAtFrontOfQueue() because we
+        // want to process the message ASAP, before any other queued
+        // messages.
+        if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING");
+            mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+        }
+    }
+
+    private final Runnable mAddStartingWindow = new Runnable() {
+
+        @Override
+        public void run() {
+            synchronized (mService.mGlobalLock) {
+                // There can only be one adding request, silly caller!
+                mService.mAnimationHandler.removeCallbacks(this);
+            }
+
+            if (startingData == null) {
+                // Animation has been canceled... do nothing.
+                if (DEBUG_STARTING_WINDOW) {
+                    Slog.v(TAG, "startingData was nulled out before handling"
+                            + " mAddStartingWindow: " + AppWindowToken.this);
+                }
+                return;
+            }
+
+            if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG, "Add starting " + this + ": startingData=" + startingData);
+            }
+
+            WindowManagerPolicy.StartingSurface surface = null;
+            try {
+                surface = startingData.createStartingSurface(AppWindowToken.this);
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception when adding starting window", e);
+            }
+            if (surface != null) {
+                boolean abort = false;
+                synchronized (mService.mGlobalLock) {
+                    // If the window was successfully added, then
+                    // we need to remove it.
+                    if (removed || startingData == null) {
+                        if (DEBUG_STARTING_WINDOW) {
+                            Slog.v(TAG, "Aborted starting " + AppWindowToken.this
+                                    + ": removed=" + removed + " startingData=" + startingData);
+                        }
+                        startingWindow = null;
+                        startingData = null;
+                        abort = true;
+                    } else {
+                        startingSurface = surface;
+                    }
+                    if (DEBUG_STARTING_WINDOW && !abort) {
+                        Slog.v(TAG, "Added starting " + AppWindowToken.this + ": startingWindow="
+                                + startingWindow + " startingView=" + startingSurface);
+                    }
+                }
+                if (abort) {
+                    surface.remove();
+                }
+            } else if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG, "Surface returned was null: " + AppWindowToken.this);
+            }
+        }
+    };
+
+    private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
+            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
+            ActivityManager.TaskSnapshot snapshot) {
+        if (getDisplayContent().mAppTransition.getAppTransition()
+                == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+            // TODO(b/34099271): Remove this statement to add back the starting window and figure
+            // out why it causes flickering, the starting window appears over the thumbnail while
+            // the docked from recents transition occurs
+            return STARTING_WINDOW_TYPE_NONE;
+        } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+            return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+        } else if (taskSwitch && allowTaskSnapshot) {
+            return snapshot == null ? STARTING_WINDOW_TYPE_NONE
+                    : snapshotOrientationSameAsTask(snapshot) || fromRecents
+                            ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+        } else {
+            return STARTING_WINDOW_TYPE_NONE;
+        }
+    }
+
+
+    private boolean snapshotOrientationSameAsTask(ActivityManager.TaskSnapshot snapshot) {
+        if (snapshot == null) {
+            return false;
+        }
+        return getTask().getConfiguration().orientation == snapshot.getOrientation();
+    }
+
+    void removeStartingWindow() {
+        if (startingWindow == null) {
+            if (startingData != null) {
+                // Starting window has not been added yet, but it is scheduled to be added.
+                // Go ahead and cancel the request.
+                if (DEBUG_STARTING_WINDOW) {
+                    Slog.v(TAG_WM, "Clearing startingData for token=" + this);
+                }
+                startingData = null;
+            }
+            return;
+        }
+
+        final WindowManagerPolicy.StartingSurface surface;
+        if (startingData != null) {
+            surface = startingSurface;
+            startingData = null;
+            startingSurface = null;
+            startingWindow = null;
+            startingDisplayed = false;
+            if (surface == null) {
+                if (DEBUG_STARTING_WINDOW) {
+                    Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
+                            + "remove");
+                }
+                return;
+            }
+        } else {
+            if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
+                        + this);
+            }
+            return;
+        }
+
+        if (DEBUG_STARTING_WINDOW) {
+            Slog.v(TAG_WM, "Schedule remove starting " + this
+                    + " startingWindow=" + startingWindow
+                    + " startingView=" + startingSurface
+                    + " Callers=" + Debug.getCallers(5));
+        }
+
+        // Use the same thread to remove the window as we used to add it, as otherwise we end up
+        // with things in the view hierarchy being called from different threads.
+        mService.mAnimationHandler.post(() -> {
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface);
+            try {
+                surface.remove();
+            } catch (Exception e) {
+                Slog.w(TAG_WM, "Exception when removing starting window", e);
+            }
+        });
+    }
+
     @Override
     boolean fillsParent() {
         return mFillsParent;
@@ -2215,9 +2622,6 @@
         if (mPendingRelaunchCount != 0) {
             pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
         }
-        if (getController() != null) {
-            pw.print(prefix); pw.print("controller="); pw.println(getController());
-        }
         if (mRemovingFromDisplay) {
             pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
         }
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 92ea1a9..08b1ae0 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -6,21 +6,16 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-import android.app.ActivityManager;
 import android.os.Debug;
 import android.os.IBinder;
-import android.os.RemoteException;
 import android.util.Slog;
+import android.view.InputApplicationHandle;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
-import android.view.InputApplicationHandle;
 import com.android.server.input.InputManagerService;
-import android.view.InputWindowHandle;
-import android.view.InputChannel;
 
 import java.io.PrintWriter;
-import java.util.HashMap;
 
 final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
     private final WindowManagerService mService;
@@ -116,9 +111,7 @@
         if (appWindowToken != null && appWindowToken.appToken != null) {
             // Notify the activity manager about the timeout and let it decide whether
             // to abort dispatching or keep waiting.
-            final AppWindowContainerController controller = appWindowToken.getController();
-            final boolean abort = controller != null
-                    && controller.keyDispatchingTimedOut(reason,
+            final boolean abort = appWindowToken.keyDispatchingTimedOut(reason,
                     (windowState != null) ? windowState.mSession.mPid : -1);
             if (!abort) {
                 // The activity manager declined to abort dispatching.
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b483fd3..c3296ce 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -497,9 +497,8 @@
                     if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
                             "RECOVER DESTROY", false);
                     winAnimator.destroySurface();
-                    if (winAnimator.mWin.mAppToken != null
-                            && winAnimator.mWin.mAppToken.getController() != null) {
-                        winAnimator.mWin.mAppToken.getController().removeStartingWindow();
+                    if (winAnimator.mWin.mAppToken != null) {
+                        winAnimator.mWin.mAppToken.removeStartingWindow();
                     }
                 }
 
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index eec10ab..932f739 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1182,7 +1182,7 @@
         mActivities.add(newTop);
 
         // Make sure window manager is aware of the position change.
-        mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
+        mWindowContainerController.positionChildAtTop(newTop.mAppWindowToken);
         updateEffectiveIntent();
 
         setFrontOfTask();
@@ -1264,12 +1264,10 @@
             mService.notifyTaskPersisterLocked(this, false);
         }
 
-        // Sync. with window manager
-        final AppWindowContainerController appController = r.getWindowContainerController();
-        if (appController != null) {
+        if (r.mAppWindowToken != null) {
             // Only attempt to move in WM if the child has a controller. It is possible we haven't
             // created controller for the activity we are starting yet.
-            mWindowContainerController.positionChildAt(appController, index);
+            mWindowContainerController.positionChildAt(r.mAppWindowToken, index);
         }
 
         // Make sure the list of display UID whitelists is updated
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 59b2055..ec64d2e 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -16,6 +16,14 @@
 
 package com.android.server.wm;
 
+import static com.android.server.EventLogTags.WM_TASK_CREATED;
+import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Rect;
@@ -24,18 +32,11 @@
 import android.os.Message;
 import android.util.EventLog;
 import android.util.Slog;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.ref.WeakReference;
 
-import static com.android.server.EventLogTags.WM_TASK_CREATED;
-import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
 /**
  * Controller for the task container. This is created by activity manager to link task records to
  * the task container they use in window manager.
@@ -103,16 +104,15 @@
         }
     }
 
-    public void positionChildAtTop(AppWindowContainerController childController) {
-        positionChildAt(childController, POSITION_TOP);
+    void positionChildAtTop(AppWindowToken aToken) {
+        positionChildAt(aToken, POSITION_TOP);
     }
 
-    public void positionChildAt(AppWindowContainerController childController, int position) {
+    void positionChildAt(AppWindowToken aToken, int position) {
         synchronized (mService.mGlobalLock) {
-            final AppWindowToken aToken = childController.mContainer;
             if (aToken == null) {
                 Slog.w(TAG_WM,
-                        "Attempted to position of non-existing app : " + childController);
+                        "Attempted to position of non-existing app");
                 return;
             }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
index f12619c..19d18ca 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
@@ -35,7 +35,6 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.Display;
-import android.view.IApplicationToken;
 
 import androidx.test.filters.SmallTest;
 
@@ -111,16 +110,9 @@
         final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
 
-        // Set TestAppWindowContainerController & assign first app token state to be good to go.
-        final WindowTestUtils.TestAppWindowContainerController controller1 =
-                createAppWindowController(dc1, token1.appToken);
-        final WindowTestUtils.TestAppWindowContainerController controller2 =
-                createAppWindowController(dc1, token2.appToken);
-        controller1.setContainer(token1);
         token1.allDrawn = true;
         token1.startingDisplayed = true;
         token1.startingMoved = true;
-        controller2.setContainer(token2);
 
         // Simulate activity resume / finish flows to prepare app transition & set visibility,
         // make sure transition is set as expected for each display.
@@ -132,8 +124,8 @@
         assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition());
         // One activity window is visible for resuming & the other activity window is invisible
         // for finishing in different display.
-        controller1.setVisibility(true, false);
-        controller2.setVisibility(false, false);
+        token1.setVisibility(true, false);
+        token2.setVisibility(false, false);
 
         // Make sure each display is in animating stage.
         assertTrue(dc1.mOpeningApps.size() > 0);
@@ -174,16 +166,4 @@
         assertFalse(dc1.mOpeningApps.contains(token1));
     }
 
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
-            DisplayContent dc, IApplicationToken token) {
-        return createAppWindowController(
-                new WindowTestUtils.TestTaskWindowContainerController(
-                        createStackControllerOnDisplay(dc)), token);
-    }
-
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
-            WindowTestUtils.TestTaskWindowContainerController taskController,
-            IApplicationToken token) {
-        return new WindowTestUtils.TestAppWindowContainerController(taskController, token);
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
deleted file mode 100644
index 415b5d9..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (C) 2017 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 static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.res.Configuration.EMPTY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController;
-
-import org.junit.Test;
-
-/**
- * Test class for {@link AppWindowContainerController}.
- *
- * atest FrameworksServicesTests:AppWindowContainerControllerTests
- */
-@FlakyTest(bugId = 74078662)
-@SmallTest
-@Presubmit
-public class AppWindowContainerControllerTests extends WindowTestsBase {
-
-    private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
-
-    @Test
-    public void testRemoveContainer() {
-        final WindowTestUtils.TestAppWindowContainerController controller =
-                createAppWindowController();
-
-        // Assert token was added to display.
-        assertNotNull(mDisplayContent.getWindowToken(controller.mToken.asBinder()));
-        // Assert that the container was created and linked.
-        assertNotNull(controller.mContainer);
-
-        controller.removeContainer(mDisplayContent.getDisplayId());
-
-        // Assert token was remove from display.
-        assertNull(mDisplayContent.getWindowToken(controller.mToken.asBinder()));
-        // Assert that the container was removed.
-        assertNull(controller.mContainer);
-    }
-
-    @Test
-    public void testSetOrientation() {
-        final WindowTestUtils.TestAppWindowContainerController controller =
-                createAppWindowController();
-
-        // Assert orientation is unspecified to start.
-        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
-
-        controller.setOrientation(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getDisplayId(),
-                EMPTY /* displayConfig */, false /* freezeScreenIfNeeded */);
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, controller.getOrientation());
-
-        controller.removeContainer(mDisplayContent.getDisplayId());
-        // Assert orientation is unspecified to after container is removed.
-        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
-
-        // Reset display frozen state
-        mWm.mDisplayFrozen = false;
-    }
-
-    private void assertHasStartingWindow(AppWindowToken atoken) {
-        assertNotNull(atoken.startingSurface);
-        assertNotNull(atoken.startingData);
-        assertNotNull(atoken.startingWindow);
-    }
-
-    private void assertNoStartingWindow(AppWindowToken atoken) {
-        assertNull(atoken.startingSurface);
-        assertNull(atoken.startingWindow);
-        assertNull(atoken.startingData);
-        atoken.forAllWindows(windowState -> {
-            assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
-        }, true);
-    }
-
-    @Test
-    public void testCreateRemoveStartingWindow() {
-        final WindowTestUtils.TestAppWindowContainerController controller =
-                createAppWindowController();
-        controller.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-        final AppWindowToken atoken = controller.getAppWindowToken(mDisplayContent);
-        assertHasStartingWindow(atoken);
-        controller.removeStartingWindow();
-        waitUntilHandlersIdle();
-        assertNoStartingWindow(atoken);
-    }
-
-    @Test
-    public void testAddRemoveRace() {
-        // There was once a race condition between adding and removing starting windows
-        for (int i = 0; i < 1000; i++) {
-            final WindowTestUtils.TestAppWindowContainerController controller =
-                    createAppWindowController();
-            controller.addStartingWindow(mPackageName,
-                    android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                    false, false);
-            controller.removeStartingWindow();
-            waitUntilHandlersIdle();
-            assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
-
-            controller.getAppWindowToken(
-                    mDisplayContent).getParent().getParent().removeImmediately();
-        }
-    }
-
-    @Test
-    public void testTransferStartingWindow() {
-        final WindowTestUtils.TestAppWindowContainerController controller1 =
-                createAppWindowController();
-        final WindowTestUtils.TestAppWindowContainerController controller2 =
-                createAppWindowController();
-        controller1.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-        controller2.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
-                true, true, false, true, false, false);
-        waitUntilHandlersIdle();
-        assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
-        assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
-    }
-
-    @Test
-    public void testTransferStartingWindowWhileCreating() {
-        final WindowTestUtils.TestAppWindowContainerController controller1 =
-                createAppWindowController();
-        final WindowTestUtils.TestAppWindowContainerController controller2 =
-                createAppWindowController();
-        ((TestWindowManagerPolicy) mWm.mPolicy).setRunnableWhenAddingSplashScreen(() -> {
-
-            // Surprise, ...! Transfer window in the middle of the creation flow.
-            controller2.addStartingWindow(mPackageName,
-                    android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
-                    true, true, false, true, false, false);
-        });
-        controller1.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-        assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
-        assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
-    }
-
-    @Test
-    public void testTryTransferStartingWindowFromHiddenAboveToken() {
-
-        // Add two tasks on top of each other.
-        TestTaskWindowContainerController taskController =
-                new WindowTestUtils.TestTaskWindowContainerController(this);
-        final WindowTestUtils.TestAppWindowContainerController controllerTop =
-                createAppWindowController(taskController);
-        final WindowTestUtils.TestAppWindowContainerController controllerBottom =
-                createAppWindowController(taskController);
-
-        // Add a starting window.
-        controllerTop.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-
-        // Make the top one invisible, and try transfering the starting window from the top to the
-        // bottom one.
-        controllerTop.setVisibility(false, false);
-        controllerBottom.mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
-
-        // Assert that the bottom window now has the starting window.
-        assertNoStartingWindow(controllerTop.getAppWindowToken(mDisplayContent));
-        assertHasStartingWindow(controllerBottom.getAppWindowToken(mDisplayContent));
-    }
-
-    @Test
-    public void testReparent() {
-        final StackWindowController stackController =
-            createStackControllerOnDisplay(mDisplayContent);
-        final WindowTestUtils.TestTaskWindowContainerController taskController1 =
-                new WindowTestUtils.TestTaskWindowContainerController(stackController);
-        final WindowTestUtils.TestAppWindowContainerController appWindowController1 =
-                createAppWindowController(taskController1);
-        final WindowTestUtils.TestTaskWindowContainerController taskController2 =
-                new WindowTestUtils.TestTaskWindowContainerController(stackController);
-        final WindowTestUtils.TestAppWindowContainerController appWindowController2 =
-                createAppWindowController(taskController2);
-        final WindowTestUtils.TestTaskWindowContainerController taskController3 =
-                new WindowTestUtils.TestTaskWindowContainerController(stackController);
-
-        try {
-            appWindowController1.reparent(taskController1, 0);
-            fail("Should not be able to reparent to the same parent");
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
-
-        try {
-            taskController3.setContainer(null);
-            appWindowController1.reparent(taskController3, 0);
-            fail("Should not be able to reparent to a task that doesn't have a container");
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
-
-        // Reparent the app window and ensure that it is moved
-        appWindowController1.reparent(taskController2, 0);
-        assertEquals(taskController2.mContainer, appWindowController1.mContainer.getParent());
-        assertEquals(0, ((WindowTestUtils.TestAppWindowToken) appWindowController1.mContainer)
-                .positionInParent());
-        assertEquals(1, ((WindowTestUtils.TestAppWindowToken) appWindowController2.mContainer)
-                .positionInParent());
-    }
-
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController() {
-        return createAppWindowController(
-                new WindowTestUtils.TestTaskWindowContainerController(this));
-    }
-
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
-            WindowTestUtils.TestTaskWindowContainerController taskController) {
-        return new WindowTestUtils.TestAppWindowContainerController(taskController);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index 552390d..99abbf7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -31,8 +32,11 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_UNSET;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -66,6 +70,8 @@
     Task mTask;
     WindowTestUtils.TestAppWindowToken mToken;
 
+    private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
+
     @Before
     public void setUp() throws Exception {
         mStack = createTaskStackOnDisplay(mDisplayContent);
@@ -251,7 +257,7 @@
                 "closingWindow");
         closingWindow.mAnimatingExit = true;
         closingWindow.mRemoveOnExit = true;
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
 
         // We pretended that we were running an exit animation, but that should have been cleared up
@@ -261,6 +267,124 @@
     }
 
     @Test
+    public void testSetOrientation() {
+        // Assert orientation is unspecified to start.
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mToken.getOrientation());
+
+        mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
+
+        mDisplayContent.removeAppToken(mToken.token);
+        // Assert orientation is unset to after container is removed.
+        assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
+
+        // Reset display frozen state
+        mWm.mDisplayFrozen = false;
+    }
+
+    @Test
+    public void testCreateRemoveStartingWindow() {
+        mToken.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+        assertHasStartingWindow(mToken);
+        mToken.removeStartingWindow();
+        waitUntilHandlersIdle();
+        assertNoStartingWindow(mToken);
+    }
+
+    @Test
+    public void testAddRemoveRace() {
+        // There was once a race condition between adding and removing starting windows
+        for (int i = 0; i < 1000; i++) {
+            final WindowTestUtils.TestAppWindowToken appToken = createIsolatedTestAppWindowToken();
+
+            appToken.addStartingWindow(mPackageName,
+                    android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                    false, false);
+            appToken.removeStartingWindow();
+            waitUntilHandlersIdle();
+            assertNoStartingWindow(appToken);
+
+            appToken.getParent().getParent().removeImmediately();
+        }
+    }
+
+    @Test
+    public void testTransferStartingWindow() {
+        final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
+        final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
+        token1.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+        token2.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, token1.appToken.asBinder(),
+                true, true, false, true, false, false);
+        waitUntilHandlersIdle();
+        assertNoStartingWindow(token1);
+        assertHasStartingWindow(token2);
+    }
+
+    @Test
+    public void testTransferStartingWindowWhileCreating() {
+        final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
+        final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
+        ((TestWindowManagerPolicy) token1.mService.mPolicy).setRunnableWhenAddingSplashScreen(
+                () -> {
+                    // Surprise, ...! Transfer window in the middle of the creation flow.
+                    token2.addStartingWindow(mPackageName,
+                            android.R.style.Theme, null, "Test", 0, 0, 0, 0,
+                            token1.appToken.asBinder(), true, true, false,
+                            true, false, false);
+                });
+        token1.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+        assertNoStartingWindow(token1);
+        assertHasStartingWindow(token2);
+    }
+
+    private WindowTestUtils.TestAppWindowToken createIsolatedTestAppWindowToken() {
+        final TaskStack taskStack = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTaskInStack(taskStack, 0 /* userId */);
+        return createTestAppWindowTokenForGivenTask(task);
+    }
+
+    private WindowTestUtils.TestAppWindowToken createTestAppWindowTokenForGivenTask(Task task) {
+        final WindowTestUtils.TestAppWindowToken appToken =
+                WindowTestUtils.createTestAppWindowToken(mDisplayContent);
+        task.addChild(appToken, 0);
+        waitUntilHandlersIdle();
+        return appToken;
+    }
+
+    @Test
+    public void testTryTransferStartingWindowFromHiddenAboveToken() {
+        // Add two tasks on top of each other.
+        final WindowTestUtils.TestAppWindowToken tokenTop = createIsolatedTestAppWindowToken();
+        final WindowTestUtils.TestAppWindowToken tokenBottom =
+                createTestAppWindowTokenForGivenTask(tokenTop.getTask());
+
+        // Add a starting window.
+        tokenTop.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+
+        // Make the top one invisible, and try transferring the starting window from the top to the
+        // bottom one.
+        tokenTop.setVisibility(false, false);
+        tokenBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded();
+
+        // Assert that the bottom window now has the starting window.
+        assertNoStartingWindow(tokenTop);
+        assertHasStartingWindow(tokenBottom);
+    }
+
+    @Test
     public void testTransitionAnimationPositionAndBounds() {
         final Rect stackBounds = new Rect(
                 0/* left */, 0 /* top */, 1000 /* right */, 1000 /* bottom */);
@@ -285,4 +409,19 @@
         assertEquals(expectedY, outPosition.y);
         assertEquals(expectedBounds, outBounds);
     }
+
+    private void assertHasStartingWindow(AppWindowToken atoken) {
+        assertNotNull(atoken.startingSurface);
+        assertNotNull(atoken.startingData);
+        assertNotNull(atoken.startingWindow);
+    }
+
+    private void assertNoStartingWindow(AppWindowToken atoken) {
+        assertNull(atoken.startingSurface);
+        assertNull(atoken.startingWindow);
+        assertNull(atoken.startingData);
+        atoken.forAllWindows(windowState -> {
+            assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
+        }, true);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index d2c0765..792e8a6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -48,7 +48,7 @@
     public void testGetClosingApps_closing() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
@@ -64,9 +64,9 @@
                 "closingWindow");
         final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
                 FIRST_APPLICATION_WINDOW, "openingWindow");
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
-        openingWindow.mAppToken.setVisibility(null, true /* visible */, TRANSIT_UNSET,
+        openingWindow.mAppToken.commitVisibility(null, true /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
@@ -79,7 +79,7 @@
     public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index 1af79e4..bbf508d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -17,8 +17,6 @@
 package com.android.server.wm;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.platform.test.annotations.Presubmit;
@@ -37,6 +35,7 @@
 @Presubmit
 public class TaskWindowContainerControllerTests extends WindowTestsBase {
 
+    /* Comment out due to removal of AppWindowContainerController
     @Test
     public void testRemoveContainer() {
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -49,7 +48,9 @@
         assertNull(taskController.mContainer);
         assertNull(appController.mContainer);
     }
+    */
 
+    /* Comment out due to removal of AppWindowContainerController
     @Test
     public void testRemoveContainer_deferRemoval() {
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -74,6 +75,7 @@
         assertNull(appController.mContainer);
         assertNull(app.getController());
     }
+    */
 
     @Test
     public void testReparent() {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 60c0459..2e47c35 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -18,7 +18,6 @@
 
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -116,12 +115,11 @@
                 ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
                 long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
                 int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
-                boolean launchTaskBehind, boolean alwaysFocusable,
-                AppWindowContainerController controller) {
+                boolean launchTaskBehind, boolean alwaysFocusable, ActivityRecord activityRecord) {
             super(service, token, activityComponent, voiceInteraction, dc,
                     inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
                     orientation, rotationAnimationHint, configChanges, launchTaskBehind,
-                    alwaysFocusable, controller);
+                    alwaysFocusable, activityRecord);
         }
 
         int getWindowsCount() {
@@ -275,45 +273,6 @@
         }
     }
 
-    public static class TestAppWindowContainerController extends AppWindowContainerController {
-
-        final IApplicationToken mToken;
-
-        TestAppWindowContainerController(TestTaskWindowContainerController taskController) {
-            this(taskController, new TestIApplicationToken());
-        }
-
-        TestAppWindowContainerController(TestTaskWindowContainerController taskController,
-                IApplicationToken token) {
-            super(taskController, token, new ComponentName("", "") /* activityComponent */,
-                    null /* listener */, 0 /* index */, SCREEN_ORIENTATION_UNSPECIFIED,
-                    true /* fullscreen */, true /* showForAllUsers */, 0 /* configChanges */,
-                    false /* voiceInteraction */, false /* launchTaskBehind */,
-                    false /* alwaysFocusable */, 0 /* targetSdkVersion */,
-                    0 /* rotationAnimationHint */, 0 /* inputDispatchingTimeoutNanos */,
-                    taskController.mService);
-            mToken = token;
-        }
-
-        @Override
-        AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
-                ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
-                long inputDispatchingTimeoutNanos,
-                boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
-                int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
-                boolean alwaysFocusable, AppWindowContainerController controller) {
-            return new TestAppWindowToken(service, token, activityComponent, voiceInteraction, dc,
-                    inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
-                    orientation,
-                    rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
-                    controller);
-        }
-
-        AppWindowToken getAppWindowToken(DisplayContent dc) {
-            return (AppWindowToken) dc.getWindowToken(mToken.asBinder());
-        }
-    }
-
     public static class TestIApplicationToken implements IApplicationToken {
 
         private final Binder mBinder = new Binder();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 170bd33..d87b571 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -29,10 +29,9 @@
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
 
-import static junit.framework.TestCase.assertNotNull;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index c2ab3ac..4a57c7d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -231,7 +231,7 @@
                     aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
                     0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
                     mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
-            activity.mWindowContainerController = mock(AppWindowContainerController.class);
+            activity.mAppWindowToken = mock(AppWindowToken.class);
 
             if (mTaskRecord != null) {
                 mTaskRecord.addActivityToTop(activity);
@@ -243,6 +243,7 @@
                     mock(WindowProcessListener.class));
             wpc.setThread(mock(IApplicationThread.class));
             activity.setProcess(wpc);
+            activity.service.mWindowManager.mRoot = mock(RootWindowContainer.class);
             return activity;
         }
     }