Merge "Revert "Add WindowDecorViewHost and WindowDecorViewHostSupplier"" into main
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 308bd0b..96a0775 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -18,7 +18,6 @@
 
 import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.content.Context;
@@ -117,8 +116,6 @@
 import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel;
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
-import com.android.wm.shell.windowdecor.viewhost.DefaultWindowDecorViewHostSupplier;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import dagger.Binds;
 import dagger.Lazy;
@@ -250,8 +247,7 @@
             MultiInstanceHelper multiInstanceHelper,
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
-            Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler) {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             return new DesktopModeWindowDecorViewModel(
                     context,
@@ -276,8 +272,7 @@
                     multiInstanceHelper,
                     desktopTasksLimiter,
                     windowDecorCaptionHandleRepository,
-                    desktopActivityOrientationHandler,
-                    windowDecorViewHostSupplier);
+                    desktopActivityOrientationHandler);
         }
         return new CaptionWindowDecorViewModel(
                 context,
@@ -291,8 +286,7 @@
                 displayController,
                 rootTaskDisplayAreaOrganizer,
                 syncQueue,
-                transitions,
-                windowDecorViewHostSupplier);
+                transitions);
     }
 
     @WMSingleton
@@ -383,13 +377,6 @@
                 context, shellInit, transitions, windowDecorViewModel);
     }
 
-    @WMSingleton
-    @Provides
-    static WindowDecorViewHostSupplier provideWindowDecorViewHostSupplier(
-            @ShellMainThread @NonNull CoroutineScope mainScope) {
-        return new DefaultWindowDecorViewHostSupplier(mainScope);
-    }
-
     //
     // One handed mode
     //
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 431461a..f5b2340 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -63,7 +63,6 @@
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 /**
  * View model for the window decoration with a caption and shadows. Works with
@@ -85,7 +84,6 @@
     private final Transitions mTransitions;
     private final Region mExclusionRegion = Region.obtain();
     private final InputManager mInputManager;
-    private final WindowDecorViewHostSupplier mWindowDecorViewHostSupplier;
     private TaskOperations mTaskOperations;
 
     /**
@@ -123,8 +121,7 @@
             DisplayController displayController,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             SyncTransactionQueue syncQueue,
-            Transitions transitions,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            Transitions transitions) {
         mContext = context;
         mMainExecutor = shellExecutor;
         mMainHandler = mainHandler;
@@ -136,7 +133,6 @@
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
         mSyncQueue = syncQueue;
         mTransitions = transitions;
-        mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
         if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
             mTaskOperations = new TaskOperations(null, mContext, mSyncQueue);
         }
@@ -300,8 +296,7 @@
                         mMainHandler,
                         mBgExecutor,
                         mMainChoreographer,
-                        mSyncQueue,
-                        mWindowDecorViewHostSupplier);
+                        mSyncQueue);
         mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
 
         final FluidResizeTaskPositioner taskPositioner =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index d0eba23..05065be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -58,7 +58,6 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 /**
  * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
@@ -90,10 +89,8 @@
             Handler handler,
             @ShellBackgroundThread ShellExecutor bgExecutor,
             Choreographer choreographer,
-            SyncTransactionQueue syncQueue,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
-        super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
-                windowDecorViewHostSupplier);
+            SyncTransactionQueue syncQueue) {
+        super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface);
         mHandler = handler;
         mBgExecutor = bgExecutor;
         mChoreographer = choreographer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index c59d929..272508f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -130,7 +130,6 @@
 import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import kotlin.Pair;
 import kotlin.Unit;
@@ -169,7 +168,6 @@
     private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
     private final Optional<DesktopTasksLimiter> mDesktopTasksLimiter;
     private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
-    private final WindowDecorViewHostSupplier mWindowDecorViewHostSupplier;
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -239,8 +237,7 @@
             MultiInstanceHelper multiInstanceHelper,
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
-            Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler) {
         this(
                 context,
                 shellExecutor,
@@ -260,7 +257,6 @@
                 genericLinksParser,
                 assistContentRequester,
                 multiInstanceHelper,
-                windowDecorViewHostSupplier,
                 new DesktopModeWindowDecoration.Factory(),
                 new InputMonitorFactory(),
                 SurfaceControl.Transaction::new,
@@ -294,7 +290,6 @@
             AppToWebGenericLinksParser genericLinksParser,
             AssistContentRequester assistContentRequester,
             MultiInstanceHelper multiInstanceHelper,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier,
             DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
             InputMonitorFactory inputMonitorFactory,
             Supplier<SurfaceControl.Transaction> transactionFactory,
@@ -322,7 +317,6 @@
         mMultiInstanceHelper = multiInstanceHelper;
         mShellCommandHandler = shellCommandHandler;
         mWindowManager = windowManager;
-        mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
         mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
         mInputMonitorFactory = inputMonitorFactory;
         mTransactionFactory = transactionFactory;
@@ -1412,8 +1406,7 @@
                         mGenericLinksParser,
                         mAssistContentRequester,
                         mMultiInstanceHelper,
-                        mWindowDecorCaptionHandleRepository,
-                        mWindowDecorViewHostSupplier);
+                        mWindowDecorCaptionHandleRepository);
         mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
 
         final TaskPositioner taskPositioner = mTaskPositionerFactory.create(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 23f8e6e..8a53f5b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -38,6 +38,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.WindowConfiguration.WindowingMode;
 import android.app.assist.AssistContent;
@@ -98,7 +99,6 @@
 import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import kotlin.Pair;
 import kotlin.Unit;
@@ -144,9 +144,12 @@
     private Function0<Unit> mOnManageWindowsClickListener;
     private DragPositioningCallback mDragPositioningCallback;
     private DragResizeInputListener mDragResizeListener;
+    private Runnable mCurrentViewHostRunnable = null;
     private RelayoutParams mRelayoutParams = new RelayoutParams();
     private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
             new WindowDecoration.RelayoutResult<>();
+    private final Runnable mViewHostRunnable =
+            () -> updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mResult);
 
     private final Point mPositionInParent = new Point();
     private HandleMenu mHandleMenu;
@@ -203,8 +206,7 @@
             AppToWebGenericLinksParser genericLinksParser,
             AssistContentRequester assistContentRequester,
             MultiInstanceHelper multiInstanceHelper,
-            WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) {
         this (context, userContext, displayController, splitScreenController, taskOrganizer,
                 taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue,
                 appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser,
@@ -212,7 +214,7 @@
                 SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
                 WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
                         context.getSystemService(WindowManager.class)),
-                new SurfaceControlViewHostFactory() {}, windowDecorViewHostSupplier,
+                new SurfaceControlViewHostFactory() {},
                 DefaultMaximizeMenuFactory.INSTANCE,
                 DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper,
                 windowDecorCaptionHandleRepository);
@@ -240,7 +242,6 @@
             Supplier<SurfaceControl> surfaceControlSupplier,
             WindowManagerWrapper windowManagerWrapper,
             SurfaceControlViewHostFactory surfaceControlViewHostFactory,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier,
             MaximizeMenuFactory maximizeMenuFactory,
             HandleMenuFactory handleMenuFactory,
             MultiInstanceHelper multiInstanceHelper,
@@ -248,7 +249,7 @@
         super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
                 surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
                 windowContainerTransactionSupplier, surfaceControlSupplier,
-                surfaceControlViewHostFactory, windowDecorViewHostSupplier);
+                surfaceControlViewHostFactory);
         mSplitScreenController = splitScreenController;
         mHandler = handler;
         mBgExecutor = bgExecutor;
@@ -362,6 +363,73 @@
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
             boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
         Trace.beginSection("DesktopModeWindowDecoration#relayout");
+        if (taskInfo.isFreeform()) {
+            // The Task is in Freeform mode -> show its header in sync since it's an integral part
+            // of the window itself - a delayed header might cause bad UX.
+            relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw,
+                    shouldSetTaskPositionAndCrop);
+        } else {
+            // The Task is outside Freeform mode -> allow the handle view to be delayed since the
+            // handle is just a small addition to the window.
+            relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw,
+                    shouldSetTaskPositionAndCrop);
+        }
+        Trace.endSection();
+    }
+
+    /** Run the whole relayout phase immediately without delay. */
+    private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+        // Clear the current ViewHost runnable as we will update the ViewHost here
+        clearCurrentViewHostRunnable();
+        updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw,
+                shouldSetTaskPositionAndCrop);
+        if (mResult.mRootView != null) {
+            updateViewHost(mRelayoutParams, startT, mResult);
+        }
+    }
+
+    /**
+     * Clear the current ViewHost runnable - to ensure it doesn't run once relayout params have been
+     * updated.
+     */
+    private void clearCurrentViewHostRunnable() {
+        if (mCurrentViewHostRunnable != null) {
+            mHandler.removeCallbacks(mCurrentViewHostRunnable);
+            mCurrentViewHostRunnable = null;
+        }
+    }
+
+    /**
+     * Relayout the window decoration but repost some of the work, to unblock the current callstack.
+     */
+    private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+        if (applyStartTransactionOnDraw) {
+            throw new IllegalArgumentException(
+                    "We cannot both sync viewhost ondraw and delay viewhost creation.");
+        }
+        // Clear the current ViewHost runnable as we will update the ViewHost here
+        clearCurrentViewHostRunnable();
+        updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT,
+                false /* applyStartTransactionOnDraw */, shouldSetTaskPositionAndCrop);
+        if (mResult.mRootView == null) {
+            // This means something blocks the window decor from showing, e.g. the task is hidden.
+            // Nothing is set up in this case including the decoration surface.
+            return;
+        }
+        // Store the current runnable so it can be removed if we start a new relayout.
+        mCurrentViewHostRunnable = mViewHostRunnable;
+        mHandler.post(mCurrentViewHostRunnable);
+    }
+
+    @SuppressLint("MissingPermission")
+    private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+        Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces");
 
         if (Flags.enableDesktopWindowingAppToWeb()) {
             setCapturedLink(taskInfo.capturedLink, taskInfo.capturedLinkTimestamp);
@@ -378,8 +446,8 @@
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
-        Trace.beginSection("DesktopModeWindowDecoration#relayout-inner");
-        relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
+        Trace.beginSection("DesktopModeWindowDecoration#relayout-updateViewsAndSurfaces");
+        updateViewsAndSurfaces(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
         Trace.endSection();
         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
 
@@ -394,7 +462,7 @@
                 notifyNoCaptionHandle();
             }
             disposeStatusBarInputLayer();
-            Trace.endSection(); // DesktopModeWindowDecoration#relayout
+            Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
             return;
         }
 
@@ -402,12 +470,12 @@
             disposeStatusBarInputLayer();
             mWindowDecorViewHolder = createViewHolder();
         }
+        Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
 
         final Point position = new Point();
         if (isAppHandle(mWindowDecorViewHolder)) {
             position.set(determineHandlePosition());
         }
-        Trace.beginSection("DesktopModeWindowDecoration#relayout-bindData");
         if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
             notifyCaptionStateChanged();
         }
@@ -425,7 +493,7 @@
         }
         updateDragResizeListener(oldDecorationSurface);
         updateMaximizeMenu(startT);
-        Trace.endSection(); // DesktopModeWindowDecoration#relayout
+        Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
     }
 
     private boolean isCaptionVisible() {
@@ -679,10 +747,6 @@
         relayoutParams.mLayoutResId = captionLayoutId;
         relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
         relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);
-        // Allow the handle view to be delayed since the handle is just a small addition to the
-        // window, whereas the header cannot be delayed because it is expected to be visible from
-        // the first frame.
-        relayoutParams.mAsyncViewHost = isAppHandle;
 
         if (isAppHeader) {
             if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
@@ -1365,10 +1429,10 @@
         mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
         disposeResizeVeil();
         disposeStatusBarInputLayer();
+        clearCurrentViewHostRunnable();
         if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
             notifyNoCaptionHandle();
         }
-
         super.close();
     }
 
@@ -1479,8 +1543,7 @@
                 AppToWebGenericLinksParser genericLinksParser,
                 AssistContentRequester assistContentRequester,
                 MultiInstanceHelper multiInstanceHelper,
-                WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
-                WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+                WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) {
             return new DesktopModeWindowDecoration(
                     context,
                     userContext,
@@ -1498,8 +1561,7 @@
                     genericLinksParser,
                     assistContentRequester,
                     multiInstanceHelper,
-                    windowDecorCaptionHandleRepository,
-                    windowDecorViewHostSupplier);
+                    windowDecorCaptionHandleRepository);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 3694845..c1a55b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -62,8 +62,6 @@
 import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement;
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer;
 import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHost;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -118,7 +116,6 @@
     final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
     final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier;
     final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
-    @NonNull private final WindowDecorViewHostSupplier mWindowDecorViewHostSupplier;
     private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
             new DisplayController.OnDisplaysChangedListener() {
                 @Override
@@ -140,7 +137,9 @@
     Context mDecorWindowContext;
     SurfaceControl mDecorationContainerSurface;
 
-    private WindowDecorViewHost mDecorViewHost;
+    SurfaceControl mCaptionContainerSurface;
+    private WindowlessWindowManager mCaptionWindowManager;
+    private SurfaceControlViewHost mViewHost;
     private Configuration mWindowDecorConfig;
     TaskDragResizer mTaskDragResizer;
     boolean mIsCaptionVisible;
@@ -159,13 +158,11 @@
             DisplayController displayController,
             ShellTaskOrganizer taskOrganizer,
             RunningTaskInfo taskInfo,
-            SurfaceControl taskSurface,
-            @NonNull WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            SurfaceControl taskSurface) {
         this(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
                 SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
                 WindowContainerTransaction::new, SurfaceControl::new,
-                new SurfaceControlViewHostFactory() {},
-                windowDecorViewHostSupplier);
+                new SurfaceControlViewHostFactory() {});
     }
 
     WindowDecoration(
@@ -179,8 +176,7 @@
             Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
             Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
             Supplier<SurfaceControl> surfaceControlSupplier,
-            SurfaceControlViewHostFactory surfaceControlViewHostFactory,
-            @NonNull WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
         mContext = context;
         mUserContext = userContext;
         mDisplayController = displayController;
@@ -191,7 +187,6 @@
         mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
         mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
         mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
-        mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
         mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
         final InsetsState insetsState = mDisplayController.getInsetsState(mTaskInfo.displayId);
         mIsStatusBarVisible = insetsState != null
@@ -217,7 +212,15 @@
     void relayout(RelayoutParams params, SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView,
             RelayoutResult<T> outResult) {
-        Trace.beginSection("WindowDecoration#relayout");
+        updateViewsAndSurfaces(params, startT, finishT, wct, rootView, outResult);
+        if (outResult.mRootView != null) {
+            updateViewHost(params, startT, outResult);
+        }
+    }
+
+    protected void updateViewsAndSurfaces(RelayoutParams params,
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+            WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult) {
         outResult.reset();
         if (params.mRunningTaskInfo != null) {
             mTaskInfo = params.mRunningTaskInfo;
@@ -228,21 +231,17 @@
         if (!mTaskInfo.isVisible) {
             releaseViews(wct);
             finishT.hide(mTaskSurface);
-            Trace.endSection(); // WindowDecoration#relayout
-            return;
-        }
-        Trace.beginSection("WindowDecoration#relayout-inflateIfNeeded");
-        inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult);
-        Trace.endSection();
-        final boolean hasCaptionView = outResult.mRootView != null;
-        if (!hasCaptionView) {
-            Trace.endSection(); // WindowDecoration#relayout
             return;
         }
 
-        Trace.beginSection("WindowDecoration#relayout-updateCaptionVisibility");
+        inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult);
+        if (outResult.mRootView == null) {
+            // Didn't manage to create a root view, early out.
+            return;
+        }
+        rootView = null; // Clear it just in case we use it accidentally
+
         updateCaptionVisibility(outResult.mRootView);
-        Trace.endSection();
 
         final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
         outResult.mWidth = taskBounds.width();
@@ -255,23 +254,10 @@
                 ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
         outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
 
-        Trace.beginSection("WindowDecoration#relayout-acquire");
-        if (mDecorViewHost == null) {
-            mDecorViewHost = mWindowDecorViewHostSupplier.acquire(mDecorWindowContext, mDisplay);
-        }
-        Trace.endSection();
-
-        final SurfaceControl captionSurface = mDecorViewHost.getSurfaceControl();
-        Trace.beginSection("WindowDecoration#relayout-updateSurfacesAndInsets");
         updateDecorationContainerSurface(startT, outResult);
-        updateCaptionContainerSurface(captionSurface, startT, outResult);
+        updateCaptionContainerSurface(startT, outResult);
         updateCaptionInsets(params, wct, outResult, taskBounds);
         updateTaskSurface(params, startT, finishT, outResult);
-        Trace.endSection();
-
-        outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
-        updateViewHierarchy(params, outResult, startT);
-        Trace.endSection(); // WindowDecoration#relayout
     }
 
     private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct,
@@ -319,32 +305,6 @@
         return (T) LayoutInflater.from(context).inflate(layoutResId, null);
     }
 
-    private void updateViewHierarchy(@NonNull RelayoutParams params,
-            @NonNull RelayoutResult<T> outResult, @NonNull SurfaceControl.Transaction startT) {
-        Trace.beginSection("WindowDecoration#updateViewHierarchy");
-        final WindowManager.LayoutParams lp =
-                new WindowManager.LayoutParams(
-                        outResult.mCaptionWidth,
-                        outResult.mCaptionHeight,
-                        TYPE_APPLICATION,
-                        FLAG_NOT_FOCUSABLE | FLAG_SPLIT_TOUCH,
-                        PixelFormat.TRANSPARENT);
-        lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
-        lp.setTrustedOverlay();
-        lp.inputFeatures = params.mInputFeatures;
-        if (params.mAsyncViewHost) {
-            if (params.mApplyStartTransactionOnDraw) {
-                throw new IllegalArgumentException(
-                        "We cannot both sync viewhost ondraw and delay viewhost creation.");
-            }
-            mDecorViewHost.updateViewAsync(outResult.mRootView, lp, mTaskInfo.getConfiguration());
-        } else {
-            mDecorViewHost.updateView(outResult.mRootView, lp, mTaskInfo.getConfiguration(),
-                    params.mApplyStartTransactionOnDraw ? startT : null);
-        }
-        Trace.endSection();
-    }
-
     private void updateDecorationContainerSurface(
             SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
         if (mDecorationContainerSurface == null) {
@@ -365,14 +325,23 @@
                 .show(mDecorationContainerSurface);
     }
 
-    private void updateCaptionContainerSurface(@NonNull SurfaceControl captionSurface,
+    private void updateCaptionContainerSurface(
             SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
-        startT.reparent(captionSurface, mDecorationContainerSurface)
-                .setWindowCrop(captionSurface, outResult.mCaptionWidth,
+        if (mCaptionContainerSurface == null) {
+            final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
+            mCaptionContainerSurface = builder
+                    .setName("Caption container of Task=" + mTaskInfo.taskId)
+                    .setContainerLayer()
+                    .setParent(mDecorationContainerSurface)
+                    .setCallsite("WindowDecoration.updateCaptionContainerSurface")
+                    .build();
+        }
+
+        startT.setWindowCrop(mCaptionContainerSurface, outResult.mCaptionWidth,
                         outResult.mCaptionHeight)
-                .setPosition(captionSurface, outResult.mCaptionX, 0 /* y */)
-                .setLayer(captionSurface, CAPTION_LAYER_Z_ORDER)
-                .show(captionSurface);
+                .setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */)
+                .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
+                .show(mCaptionContainerSurface);
     }
 
     private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct,
@@ -466,6 +435,64 @@
         }
     }
 
+    /**
+     * Updates a {@link SurfaceControlViewHost} to connect the window decoration surfaces with our
+     * View hierarchy.
+     *
+     * @param params parameters to use from the last relayout
+     * @param onDrawTransaction a transaction to apply in sync with #onDraw
+     * @param outResult results to use from the last relayout
+     *
+     */
+    protected void updateViewHost(RelayoutParams params,
+            SurfaceControl.Transaction onDrawTransaction, RelayoutResult<T> outResult) {
+        Trace.beginSection("CaptionViewHostLayout");
+        if (mCaptionWindowManager == null) {
+            // Put caption under a container surface because ViewRootImpl sets the destination frame
+            // of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
+            mCaptionWindowManager = new WindowlessWindowManager(
+                    mTaskInfo.getConfiguration(), mCaptionContainerSurface,
+                    null /* hostInputToken */);
+        }
+        mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration());
+        final WindowManager.LayoutParams lp =
+                new WindowManager.LayoutParams(
+                        outResult.mCaptionWidth,
+                        outResult.mCaptionHeight,
+                        TYPE_APPLICATION,
+                        FLAG_NOT_FOCUSABLE | FLAG_SPLIT_TOUCH,
+                        PixelFormat.TRANSPARENT);
+        lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
+        lp.setTrustedOverlay();
+        lp.inputFeatures = params.mInputFeatures;
+        if (mViewHost == null) {
+            Trace.beginSection("CaptionViewHostLayout-new");
+            mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
+                    mCaptionWindowManager);
+            if (params.mApplyStartTransactionOnDraw) {
+                if (onDrawTransaction == null) {
+                    throw new IllegalArgumentException("Trying to sync a null Transaction");
+                }
+                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
+            }
+            outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
+            mViewHost.setView(outResult.mRootView, lp);
+            Trace.endSection();
+        } else {
+            Trace.beginSection("CaptionViewHostLayout-relayout");
+            if (params.mApplyStartTransactionOnDraw) {
+                if (onDrawTransaction == null) {
+                    throw new IllegalArgumentException("Trying to sync a null Transaction");
+                }
+                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
+            }
+            outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
+            mViewHost.relayout(lp);
+            Trace.endSection();
+        }
+        Trace.endSection(); // CaptionViewHostLayout
+    }
+
     private Rect calculateBoundingRect(@NonNull OccludingCaptionElement element,
             int elementWidthPx, @NonNull Rect captionRect) {
         switch (element.mAlignment) {
@@ -553,11 +580,18 @@
     }
 
     void releaseViews(WindowContainerTransaction wct) {
+        if (mViewHost != null) {
+            mViewHost.release();
+            mViewHost = null;
+        }
+
+        mCaptionWindowManager = null;
+
         final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
         boolean released = false;
-        if (mDecorViewHost != null) {
-            mWindowDecorViewHostSupplier.release(mDecorViewHost, t);
-            mDecorViewHost = null;
+        if (mCaptionContainerSurface != null) {
+            t.remove(mCaptionContainerSurface);
+            mCaptionContainerSurface = null;
             released = true;
         }
 
@@ -708,7 +742,6 @@
 
         boolean mApplyStartTransactionOnDraw;
         boolean mSetTaskPositionAndCrop;
-        boolean mAsyncViewHost;
 
         void reset() {
             mLayoutResId = Resources.ID_NULL;
@@ -725,7 +758,6 @@
 
             mApplyStartTransactionOnDraw = false;
             mSetTaskPositionAndCrop = false;
-            mAsyncViewHost = false;
             mWindowDecorConfig = null;
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt
deleted file mode 100644
index 139e679..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wm.shell.windowdecor.viewhost
-
-import android.content.Context
-import android.content.res.Configuration
-import android.view.Display
-import android.view.SurfaceControl
-import android.view.SurfaceControlViewHost
-import android.view.View
-import android.view.WindowManager
-import android.view.WindowlessWindowManager
-import androidx.tracing.Trace
-import com.android.internal.annotations.VisibleForTesting
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-typealias SurfaceControlViewHostFactory =
-            (Context, Display, WindowlessWindowManager, String) -> SurfaceControlViewHost
-
-/**
- * A default implementation of [WindowDecorViewHost] backed by a [SurfaceControlViewHost].
- *
- * It does not support swapping the root view added to the VRI of the [SurfaceControlViewHost], and
- * any attempts to do will throw, which means that once a [View] is added using [updateView] or
- * [updateViewAsync], only its properties and binding may be changed, its children views may be
- * added, removed or changed and its [WindowManager.LayoutParams] may be changed.
- * It also supports asynchronously updating the view hierarchy using [updateViewAsync], in which
- * case the update work will be posted on the [ShellMainThread] with no delay.
- */
-class DefaultWindowDecorViewHost(
-    private val context: Context,
-    @ShellMainThread private val mainScope: CoroutineScope,
-    private val display: Display,
-    private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = { c, d, wwm, s ->
-        SurfaceControlViewHost(c, d, wwm, s)
-    }
-) : WindowDecorViewHost {
-
-    private val rootSurface: SurfaceControl = SurfaceControl.Builder()
-            .setName("DefaultWindowDecorViewHost surface")
-            .setContainerLayer()
-            .setCallsite("DefaultWindowDecorViewHost#init")
-            .build()
-
-    private var wwm: WindowlessWindowManager? = null
-    @VisibleForTesting
-    var viewHost: SurfaceControlViewHost? = null
-    private var currentUpdateJob: Job? = null
-
-    override val surfaceControl: SurfaceControl
-        get() = rootSurface
-
-    override fun updateView(
-        view: View,
-        attrs: WindowManager.LayoutParams,
-        configuration: Configuration,
-        onDrawTransaction: SurfaceControl.Transaction?
-    ) {
-        Trace.beginSection("DefaultWindowDecorViewHost#updateView")
-        clearCurrentUpdateJob()
-        updateViewHost(view, attrs, configuration, onDrawTransaction)
-        Trace.endSection()
-    }
-
-    override fun updateViewAsync(
-        view: View,
-        attrs: WindowManager.LayoutParams,
-        configuration: Configuration
-    ) {
-        Trace.beginSection("DefaultWindowDecorViewHost#updateViewAsync")
-        clearCurrentUpdateJob()
-        currentUpdateJob = mainScope.launch {
-            updateViewHost(view, attrs, configuration, onDrawTransaction = null)
-        }
-        Trace.endSection()
-    }
-
-    override fun release(t: SurfaceControl.Transaction) {
-        clearCurrentUpdateJob()
-        viewHost?.release()
-        t.remove(rootSurface)
-    }
-
-    private fun updateViewHost(
-        view: View,
-        attrs: WindowManager.LayoutParams,
-        configuration: Configuration,
-        onDrawTransaction: SurfaceControl.Transaction?
-    ) {
-        Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost")
-        if (wwm == null) {
-            wwm = WindowlessWindowManager(configuration, rootSurface, null)
-        }
-        requireWindowlessWindowManager().setConfiguration(configuration)
-        if (viewHost == null) {
-            viewHost = surfaceControlViewHostFactory.invoke(
-                context,
-                display,
-                requireWindowlessWindowManager(),
-                "DefaultWindowDecorViewHost#updateViewHost"
-            )
-        }
-        onDrawTransaction?.let {
-            requireViewHost().rootSurfaceControl.applyTransactionOnDraw(it)
-        }
-        if (requireViewHost().view == null) {
-            Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-setView")
-            requireViewHost().setView(view, attrs)
-            Trace.endSection()
-        } else {
-            check(requireViewHost().view == view) { "Changing view is not allowed" }
-            Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-relayout")
-            requireViewHost().relayout(attrs)
-            Trace.endSection()
-        }
-        Trace.endSection()
-    }
-
-    private fun clearCurrentUpdateJob() {
-        currentUpdateJob?.cancel()
-        currentUpdateJob = null
-    }
-
-    private fun requireWindowlessWindowManager(): WindowlessWindowManager {
-        return wwm ?: error("Expected non-null windowless window manager")
-    }
-
-    private fun requireViewHost(): SurfaceControlViewHost {
-        return viewHost ?: error("Expected non-null view host")
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostSupplier.kt
deleted file mode 100644
index 9997e8f..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostSupplier.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wm.shell.windowdecor.viewhost
-
-import android.content.Context
-import android.view.Display
-import android.view.SurfaceControl
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import kotlinx.coroutines.CoroutineScope
-
-/**
- * A supplier of [DefaultWindowDecorViewHost]s. It creates a new one every time one is requested.
- */
-class DefaultWindowDecorViewHostSupplier(
-    @ShellMainThread private val mainScope: CoroutineScope,
-) : WindowDecorViewHostSupplier<DefaultWindowDecorViewHost> {
-
-    override fun acquire(context: Context, display: Display): DefaultWindowDecorViewHost {
-        return DefaultWindowDecorViewHost(context, mainScope, display)
-    }
-
-    override fun release(viewHost: DefaultWindowDecorViewHost, t: SurfaceControl.Transaction) {
-        viewHost.release(t)
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHost.kt
deleted file mode 100644
index 3fbaea8..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHost.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wm.shell.windowdecor.viewhost
-
-import android.content.res.Configuration
-import android.view.SurfaceControl
-import android.view.View
-import android.view.WindowManager
-import com.android.wm.shell.windowdecor.WindowDecoration
-
-/**
- * An interface for a utility that hosts a [WindowDecoration]'s [View] hierarchy under a
- * [SurfaceControl].
- */
-interface WindowDecorViewHost {
-    /** The surface where the underlying [View] hierarchy is being rendered. */
-    val surfaceControl: SurfaceControl
-
-    /** Synchronously update the view hierarchy of this view host. */
-    fun updateView(
-        view: View,
-        attrs: WindowManager.LayoutParams,
-        configuration: Configuration,
-        onDrawTransaction: SurfaceControl.Transaction?
-    )
-
-    /** Asynchronously update the view hierarchy of this view host. */
-    fun updateViewAsync(
-        view: View,
-        attrs: WindowManager.LayoutParams,
-        configuration: Configuration
-    )
-
-    /** Releases the underlying [View] hierarchy and removes the backing [SurfaceControl]. */
-    fun release(t: SurfaceControl.Transaction)
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHostSupplier.kt
deleted file mode 100644
index 0e23584..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHostSupplier.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wm.shell.windowdecor.viewhost
-
-import android.content.Context
-import android.view.Display
-import android.view.SurfaceControl
-
-/**
- * An interface for a supplier of [WindowDecorViewHost]s.
- */
-interface WindowDecorViewHostSupplier<T : WindowDecorViewHost> {
-    /** Acquire a [WindowDecorViewHost]. */
-    fun acquire(context: Context, display: Display): T
-
-    /**
-     * Release a [WindowDecorViewHost] when it is no longer used.
-     *
-     * @param viewHost the [WindowDecorViewHost] to release
-     * @param t a transaction that may be used to remove any underlying backing [SurfaceControl]
-     *          that are hosting this [WindowDecorViewHost]. The supplier is not expected to apply
-     *          the transaction. It should be applied by the owner of this supplier.
-     */
-    fun release(viewHost: T, t: SurfaceControl.Transaction)
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index ee2a41c..3051714 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -100,7 +100,6 @@
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier
 import java.util.Optional
 import java.util.function.Consumer
 import java.util.function.Supplier
@@ -186,7 +185,6 @@
             DesktopModeWindowDecorViewModel.TaskPositionerFactory
     @Mock private lateinit var mockTaskPositioner: TaskPositioner
     @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository
-    @Mock private lateinit var mockWindowDecorViewHostSupplier: WindowDecorViewHostSupplier<*>
     private lateinit var spyContext: TestableContext
 
     private val transactionFactory = Supplier<SurfaceControl.Transaction> {
@@ -236,7 +234,6 @@
                 mockGenericLinksParser,
                 mockAssistContentRequester,
                 mockMultiInstanceHelper,
-                mockWindowDecorViewHostSupplier,
                 mockDesktopModeWindowDecorFactory,
                 mockInputMonitorFactory,
                 transactionFactory,
@@ -1217,7 +1214,7 @@
         whenever(
             mockDesktopModeWindowDecorFactory.create(
                 any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
-                any(), any(), any(), any(), any(), any(), any())
+                any(), any(), any(), any(), any(), any())
         ).thenReturn(decoration)
         decoration.mTaskInfo = task
         whenever(decoration.isFocused).thenReturn(task.isFocused)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 9c11ec3..f007115 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -47,6 +47,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.kotlin.VerificationKt.times;
 
 import android.app.ActivityManager;
 import android.app.assist.AssistContent;
@@ -106,8 +107,6 @@
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHost;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import kotlin.Unit;
 import kotlin.jvm.functions.Function0;
@@ -178,10 +177,6 @@
     @Mock
     private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
     @Mock
-    private WindowDecorViewHostSupplier mMockWindowDecorViewHostSupplier;
-    @Mock
-    private WindowDecorViewHost mMockWindowDecorViewHost;
-    @Mock
     private TypedArray mMockRoundedCornersRadiusArray;
     @Mock
     private TestTouchEventListener mMockTouchEventListener;
@@ -257,9 +252,6 @@
                 anyBoolean(), anyBoolean(), anyBoolean(), any(), anyInt(), anyInt(), anyInt()))
                 .thenReturn(mMockHandleMenu);
         when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false);
-        when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay)))
-                .thenReturn(mMockWindowDecorViewHost);
-        when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class));
         when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any(), any(),
                 any())).thenReturn(mMockAppHeaderViewHolder);
     }
@@ -533,42 +525,6 @@
     }
 
     @Test
-    public void updateRelayoutParams_handle_requestsAsyncViewHostRendering() {
-        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
-        // Make the task fullscreen so that its decoration is an App Handle.
-        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        final RelayoutParams relayoutParams = new RelayoutParams();
-
-        DesktopModeWindowDecoration.updateRelayoutParams(
-                relayoutParams,
-                mTestableContext,
-                taskInfo,
-                /* applyStartTransactionOnDraw= */ true,
-                /* shouldSetTaskPositionAndCrop */ false);
-
-        // App Handles don't need to be rendered in sync with the task animation, per UX.
-        assertThat(relayoutParams.mAsyncViewHost).isTrue();
-    }
-
-    @Test
-    public void updateRelayoutParams_header_requestsSyncViewHostRendering() {
-        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
-        // Make the task freeform so that its decoration is an App Header.
-        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        final RelayoutParams relayoutParams = new RelayoutParams();
-
-        DesktopModeWindowDecoration.updateRelayoutParams(
-                relayoutParams,
-                mTestableContext,
-                taskInfo,
-                /* applyStartTransactionOnDraw= */ true,
-                /* shouldSetTaskPositionAndCrop */ false);
-
-        // App Headers must be rendered in sync with the task animation, so it cannot be delayed.
-        assertThat(relayoutParams.mAsyncViewHost).isFalse();
-    }
-
-    @Test
     public void relayout_fullscreenTask_appliesTransactionImmediately() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -592,7 +548,78 @@
         spyWindowDecor.relayout(taskInfo);
 
         verify(mMockTransaction, never()).apply();
-        verify(mMockWindowDecorViewHost).updateView(any(), any(), any(), eq(mMockTransaction));
+        verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction);
+    }
+
+    @Test
+    public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        spyWindowDecor.relayout(taskInfo);
+
+        verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any());
+    }
+
+    @Test
+    public void relayout_fullscreenTask_postsViewHostCreation() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+        spyWindowDecor.relayout(taskInfo);
+
+        // Once for view host, the other for the AppHandle input layer.
+        verify(mMockHandler, times(2)).post(runnableArgument.capture());
+        runnableArgument.getValue().run();
+        verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
+    }
+
+    @Test
+    @Ignore("TODO(b/367235906): Due to MONITOR_INPUT permission error")
+    public void relayout_freeformTask_createsViewHostImmediately() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT)
+        taskInfo.isResizeable = false;
+
+        spyWindowDecor.relayout(taskInfo);
+
+        verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
+        verify(mMockHandler, never()).post(any());
+    }
+
+    @Test
+    public void relayout_removesExistingHandlerCallback() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+        spyWindowDecor.relayout(taskInfo);
+        // Once for view host, the other for the AppHandle input layer.
+        verify(mMockHandler, times(2)).post(runnableArgument.capture());
+
+        spyWindowDecor.relayout(taskInfo);
+
+        verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
+    }
+
+    @Test
+    public void close_removesExistingHandlerCallback() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+        spyWindowDecor.relayout(taskInfo);
+        // Once for view host, the other for the AppHandle input layer.
+        verify(mMockHandler, times(2)).post(runnableArgument.capture());
+
+        spyWindowDecor.close();
+
+        verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
     }
 
     @Test
@@ -1065,7 +1092,7 @@
                 mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
                 mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
                 new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
-                mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
+                maximizeMenuFactory, mMockHandleMenuFactory,
                 mMockMultiInstanceHelper, mMockCaptionHandleRepository);
         windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
                 mMockTouchEventListener, mMockTouchEventListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 7252b32..2e117ac 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -85,8 +85,6 @@
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.tests.R;
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHost;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -130,10 +128,6 @@
     @Mock
     private SurfaceControlViewHost mMockSurfaceControlViewHost;
     @Mock
-    private WindowDecorViewHostSupplier mMockWindowDecorViewHostSupplier;
-    @Mock
-    private WindowDecorViewHost mMockWindowDecorViewHost;
-    @Mock
     private AttachedSurfaceControl mMockRootSurfaceControl;
     @Mock
     private TestView mMockView;
@@ -173,9 +167,6 @@
         when(mMockSurfaceControlViewHost.getRootSurfaceControl())
                 .thenReturn(mMockRootSurfaceControl);
         when(mMockView.findViewById(anyInt())).thenReturn(mMockView);
-        when(mMockWindowDecorViewHostSupplier.acquire(any(), any()))
-                .thenReturn(mMockWindowDecorViewHost);
-        when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class));
 
         // Add status bar inset so that WindowDecoration does not think task is in immersive mode
         mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, statusBars()).setVisible(true);
@@ -239,6 +230,10 @@
         final SurfaceControl.Builder decorContainerSurfaceBuilder =
                 createMockSurfaceControlBuilder(decorContainerSurface);
         mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
 
         final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
                 .setDisplayId(Display.DEFAULT_DISPLAY)
@@ -259,18 +254,18 @@
         verify(mMockSurfaceControlStartT).setTrustedOverlay(decorContainerSurface, true);
         verify(mMockSurfaceControlStartT).setWindowCrop(decorContainerSurface, 300, 100);
 
-        final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl();
-        verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface);
+        verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
+        verify(captionContainerSurfaceBuilder).setContainerLayer();
         verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
         verify(mMockSurfaceControlStartT).show(captionContainerSurface);
 
-        verify(mMockWindowDecorViewHost).updateView(
-                same(mMockView),
-                argThat(lp -> lp.height == 64
-                        && lp.width == 300
-                        && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0),
-                eq(taskInfo.configuration),
-                eq(null) /* onDrawTransaction */);
+        verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any());
+
+        verify(mMockSurfaceControlViewHost)
+                .setView(same(mMockView),
+                        argThat(lp -> lp.height == 64
+                                && lp.width == 300
+                                && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
         verify(mMockView).setTaskFocusState(true);
         verify(mMockWindowContainerTransaction).addInsetsSource(
                 eq(taskInfo.token),
@@ -301,6 +296,10 @@
         final SurfaceControl.Builder decorContainerSurfaceBuilder =
                 createMockSurfaceControlBuilder(decorContainerSurface);
         mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
 
         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
         mMockSurfaceControlTransactions.add(t);
@@ -323,7 +322,7 @@
 
         windowDecor.relayout(taskInfo);
 
-        verify(mMockWindowDecorViewHost, never()).release(any());
+        verify(mMockSurfaceControlViewHost, never()).release();
         verify(t, never()).apply();
         verify(mMockWindowContainerTransaction, never())
                 .removeInsetsSource(eq(taskInfo.token), any(), anyInt(), anyInt());
@@ -333,8 +332,9 @@
         taskInfo.isVisible = false;
         windowDecor.relayout(taskInfo);
 
-        final InOrder releaseOrder = inOrder(t2, mMockWindowDecorViewHostSupplier);
-        releaseOrder.verify(mMockWindowDecorViewHostSupplier).release(mMockWindowDecorViewHost, t2);
+        final InOrder releaseOrder = inOrder(t2, mMockSurfaceControlViewHost);
+        releaseOrder.verify(mMockSurfaceControlViewHost).release();
+        releaseOrder.verify(t2).remove(captionContainerSurface);
         releaseOrder.verify(t2).remove(decorContainerSurface);
         releaseOrder.verify(t2).apply();
         // Expect to remove two insets sources, the caption insets and the mandatory gesture insets.
@@ -382,8 +382,8 @@
         verify(mMockDisplayController).removeDisplayWindowListener(same(listener));
 
         assertThat(mRelayoutResult.mRootView).isSameInstanceAs(mMockView);
-        verify(mMockWindowDecorViewHostSupplier).acquire(any(), eq(mockDisplay));
-        verify(mMockWindowDecorViewHost).updateView(same(mMockView), any(), any(), any());
+        verify(mMockSurfaceControlViewHostFactory).create(any(), eq(mockDisplay), any());
+        verify(mMockSurfaceControlViewHost).setView(same(mMockView), any());
     }
 
     @Test
@@ -396,6 +396,10 @@
         final SurfaceControl.Builder decorContainerSurfaceBuilder =
                 createMockSurfaceControlBuilder(decorContainerSurface);
         mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
 
         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
         mMockSurfaceControlTransactions.add(t);
@@ -432,7 +436,8 @@
                 windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId);
         verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, width, height);
         verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface);
-        verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any());
+        verify(mMockSurfaceControlViewHostFactory, Mockito.times(2))
+                .create(any(), eq(defaultDisplay), any());
     }
 
     @Test
@@ -445,6 +450,10 @@
         final SurfaceControl.Builder decorContainerSurfaceBuilder =
                 createMockSurfaceControlBuilder(decorContainerSurface);
         mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
 
         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
         mMockSurfaceControlTransactions.add(t);
@@ -464,8 +473,8 @@
 
         windowDecor.relayout(taskInfo);
 
-        final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl();
-        verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface);
+        verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
+        verify(captionContainerSurfaceBuilder).setContainerLayer();
         // Width of the captionContainerSurface should match the width of TASK_BOUNDS
         verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
         verify(mMockSurfaceControlStartT).show(captionContainerSurface);
@@ -481,6 +490,10 @@
         final SurfaceControl.Builder decorContainerSurfaceBuilder =
                 createMockSurfaceControlBuilder(decorContainerSurface);
         mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
 
         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
         mMockSurfaceControlTransactions.add(t);
@@ -498,11 +511,9 @@
         taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
-        mRelayoutParams.mApplyStartTransactionOnDraw = true;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */);
 
-        verify(mMockWindowDecorViewHost).updateView(any(), any(), any(),
-                eq(mMockSurfaceControlStartT));
+        verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
     }
 
     @Test
@@ -856,52 +867,37 @@
     }
 
     @Test
-    public void relayout_applyTransactionOnDrawIsTrue_updatesViewWithDrawTransaction() {
+    public void updateViewHost_applyTransactionOnDrawIsTrue_surfaceControlIsUpdated() {
         final TestWindowDecoration windowDecor = createWindowDecoration(
-                new TestRunningTaskInfoBuilder()
-                        .setVisible(true)
-                        .setWindowingMode(WINDOWING_MODE_FREEFORM)
-                        .build());
+                new TestRunningTaskInfoBuilder().build());
         mRelayoutParams.mApplyStartTransactionOnDraw = true;
         mRelayoutResult.mRootView = mMockView;
 
-        windowDecor.relayout(windowDecor.mTaskInfo);
+        windowDecor.updateViewHost(mRelayoutParams, mMockSurfaceControlStartT, mRelayoutResult);
 
-        verify(mMockWindowDecorViewHost)
-                .updateView(eq(mRelayoutResult.mRootView), any(),
-                        eq(windowDecor.mTaskInfo.configuration), eq(mMockSurfaceControlStartT));
+        verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
     }
 
     @Test
-    public void relayout_applyTransactionOnDrawIsTrue_asyncViewHostRendering_throwsException() {
+    public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsTrue_throwsException() {
         final TestWindowDecoration windowDecor = createWindowDecoration(
-                new TestRunningTaskInfoBuilder()
-                        .setVisible(true)
-                        .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
-                        .build());
+                new TestRunningTaskInfoBuilder().build());
         mRelayoutParams.mApplyStartTransactionOnDraw = true;
-        mRelayoutParams.mAsyncViewHost = true;
         mRelayoutResult.mRootView = mMockView;
 
         assertThrows(IllegalArgumentException.class,
-                () -> windowDecor.relayout(windowDecor.mTaskInfo));
+                () -> windowDecor.updateViewHost(
+                        mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult));
     }
 
     @Test
-    public void relayout_asyncViewHostRendering() {
+    public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsFalse_doesNotThrow() {
         final TestWindowDecoration windowDecor = createWindowDecoration(
-                new TestRunningTaskInfoBuilder()
-                        .setVisible(true)
-                        .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
-                        .build());
-        mRelayoutParams.mAsyncViewHost = true;
+                new TestRunningTaskInfoBuilder().build());
+        mRelayoutParams.mApplyStartTransactionOnDraw = false;
         mRelayoutResult.mRootView = mMockView;
 
-        windowDecor.relayout(windowDecor.mTaskInfo);
-
-        verify(mMockWindowDecorViewHost)
-                .updateViewAsync(eq(mRelayoutResult.mRootView), any(),
-                        eq(windowDecor.mTaskInfo.configuration));
+        windowDecor.updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult);
     }
 
     @Test
@@ -1001,8 +997,7 @@
                 new MockObjectSupplier<>(mMockSurfaceControlTransactions,
                         () -> mock(SurfaceControl.Transaction.class)),
                 () -> mMockWindowContainerTransaction, () -> mMockTaskSurface,
-                mMockSurfaceControlViewHostFactory,
-                mMockWindowDecorViewHostSupplier);
+                mMockSurfaceControlViewHostFactory);
     }
 
     private class MockObjectSupplier<T> implements Supplier<T> {
@@ -1042,20 +1037,16 @@
                 Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
                 Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
                 Supplier<SurfaceControl> surfaceControlSupplier,
-                SurfaceControlViewHostFactory surfaceControlViewHostFactory,
-                @NonNull WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+                SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
             super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
                     surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
                     windowContainerTransactionSupplier, surfaceControlSupplier,
-                    surfaceControlViewHostFactory, windowDecorViewHostSupplier);
+                    surfaceControlViewHostFactory);
         }
 
         @Override
         void relayout(ActivityManager.RunningTaskInfo taskInfo) {
-            mRelayoutParams.mRunningTaskInfo = taskInfo;
-            mRelayoutParams.mLayoutResId = R.layout.caption_layout;
-            relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
-                    mMockWindowContainerTransaction, mMockView, mRelayoutResult);
+            relayout(taskInfo, false /* applyStartTransactionOnDraw */);
         }
 
         @Override
@@ -1076,6 +1067,15 @@
             return super.inflateLayout(context, layoutResId);
         }
 
+        void relayout(ActivityManager.RunningTaskInfo taskInfo,
+                boolean applyStartTransactionOnDraw) {
+            mRelayoutParams.mRunningTaskInfo = taskInfo;
+            mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
+            mRelayoutParams.mLayoutResId = R.layout.caption_layout;
+            relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
+                    mMockWindowContainerTransaction, mMockView, mRelayoutResult);
+        }
+
         private AdditionalViewContainer addTestViewContainer() {
             final Resources resources = mDecorWindowContext.getResources();
             final int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt
deleted file mode 100644
index 1b2ce9e..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wm.shell.windowdecor.viewhost
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.SurfaceControl
-import android.view.SurfaceControlViewHost
-import android.view.View
-import android.view.WindowManager
-import androidx.test.filters.SmallTest
-import com.android.wm.shell.ShellTestCase
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.advanceUntilIdle
-import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertThrows
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
-import org.mockito.kotlin.spy
-import org.mockito.kotlin.verify
-
-
-/**
- * Tests for [DefaultWindowDecorViewHost].
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:DefaultWindowDecorViewHostTest
- */
-@SmallTest
-@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner::class)
-class DefaultWindowDecorViewHostTest : ShellTestCase() {
-
-    @Test
-    fun updateView_layoutInViewHost() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-        val view = View(context)
-
-        windowDecorViewHost.updateView(
-            view = view,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        assertThat(windowDecorViewHost.viewHost).isNotNull()
-        assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
-    }
-
-    @Test
-    fun updateView_alreadyLaidOut_relayouts() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-        val view = View(context)
-        windowDecorViewHost.updateView(
-            view = view,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        val otherParams = WindowManager.LayoutParams(200, 200)
-        windowDecorViewHost.updateView(
-            view = view,
-            attrs = otherParams,
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
-        assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
-            .isEqualTo(otherParams.width)
-    }
-
-    @Test
-    fun updateView_replacingView_throws() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-        val view = View(context)
-        windowDecorViewHost.updateView(
-            view = view,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        val otherView = View(context)
-        assertThrows(Exception::class.java) {
-            windowDecorViewHost.updateView(
-                view = otherView,
-                attrs = WindowManager.LayoutParams(100, 100),
-                configuration = context.resources.configuration,
-                onDrawTransaction = null
-            )
-        }
-    }
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    @Test
-    fun updateView_clearsPendingAsyncJob() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-        val asyncView = View(context)
-        val syncView = View(context)
-        val asyncAttrs = WindowManager.LayoutParams(100, 100)
-        val syncAttrs = WindowManager.LayoutParams(200, 200)
-
-        windowDecorViewHost.updateViewAsync(
-            view = asyncView,
-            attrs = asyncAttrs,
-            configuration = context.resources.configuration,
-        )
-
-        // No view host yet, since the coroutine hasn't run.
-        assertThat(windowDecorViewHost.viewHost).isNull()
-
-        windowDecorViewHost.updateView(
-            view = syncView,
-            attrs = syncAttrs,
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        // Would run coroutine if it hadn't been cancelled.
-        advanceUntilIdle()
-
-        assertThat(windowDecorViewHost.viewHost).isNotNull()
-        assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
-        // View host view/attrs should match the ones from the sync call, plus, since the
-        // sync/async were made with different views, if the job hadn't been cancelled there
-        // would've been an exception thrown as replacing views isn't allowed.
-        assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(syncView)
-        assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
-            .isEqualTo(syncAttrs.width)
-    }
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    @Test
-    fun updateViewAsync() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-        val view = View(context)
-        val attrs = WindowManager.LayoutParams(100, 100)
-
-        windowDecorViewHost.updateViewAsync(
-            view = view,
-            attrs = attrs,
-            configuration = context.resources.configuration,
-        )
-
-        assertThat(windowDecorViewHost.viewHost).isNull()
-
-        advanceUntilIdle()
-
-        assertThat(windowDecorViewHost.viewHost).isNotNull()
-    }
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    @Test
-    fun updateViewAsync_clearsPendingAsyncJob() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-
-        val view = View(context)
-        windowDecorViewHost.updateViewAsync(
-            view = view,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-        )
-        val otherView = View(context)
-        windowDecorViewHost.updateViewAsync(
-            view = otherView,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-        )
-
-        advanceUntilIdle()
-
-        assertThat(windowDecorViewHost.viewHost).isNotNull()
-        assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
-        assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(otherView)
-    }
-
-    @Test
-    fun release() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-
-        val view = View(context)
-        windowDecorViewHost.updateView(
-            view = view,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        val t = mock(SurfaceControl.Transaction::class.java)
-        windowDecorViewHost.release(t)
-
-        verify(windowDecorViewHost.viewHost!!).release()
-        verify(t).remove(windowDecorViewHost.surfaceControl)
-    }
-
-    private fun CoroutineScope.createDefaultViewHost() = DefaultWindowDecorViewHost(
-        context = context,
-        mainScope = this,
-        display = context.display,
-        surfaceControlViewHostFactory = { c, d, wwm, s ->
-            spy(SurfaceControlViewHost(c, d, wwm, s))
-        }
-    )
-}
\ No newline at end of file