Merge "Cleanup insets_control_seq" into main
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index 1e9a79b..4fb0601 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -680,6 +680,7 @@
         dest.writeParcelable(mStartIntent, flags);
         dest.writeInt(mLaunchMode);
         dest.writeBoolean(mWasForceStopped);
+        dest.writeLong(mMonoticCreationTimeMs);
     }
 
     /** @hide */
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fe3d4f6..07efad8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4380,7 +4380,7 @@
              modes dimensions {@link config_minPercentageMultiWindowSupportWidth} the device
              supports to determine if the activity can be shown in multi windowing modes.
     -->
-    <integer name="config_respectsActivityMinWidthHeightMultiWindow">0</integer>
+    <integer name="config_respectsActivityMinWidthHeightMultiWindow">-1</integer>
 
     <!-- This value is only used when the device checks activity min height to determine if it
          can be shown in multi windowing modes.
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
index 991cdcf..c7b4c65 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
index 991cdcf..c7b4c65 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
Binary files differ
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
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
index 70d353d..7e1df16 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.IntrinsicSize
@@ -25,11 +26,14 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.Launch
 import androidx.compose.material.icons.outlined.Delete
 import androidx.compose.material.icons.outlined.WarningAmber
 import androidx.compose.material3.ButtonDefaults
@@ -51,7 +55,7 @@
 import com.android.settingslib.spa.framework.theme.SettingsShape
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.framework.theme.divider
-import androidx.compose.material.icons.automirrored.outlined.Launch
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
 
 data class ActionButton(
     val text: String,
@@ -62,48 +66,64 @@
 
 @Composable
 fun ActionButtons(actionButtons: List<ActionButton>) {
-    Row(
-        Modifier
-            .padding(SettingsDimension.buttonPadding)
-            .clip(SettingsShape.CornerExtraLarge)
-            .height(IntrinsicSize.Min)
-    ) {
-        for ((index, actionButton) in actionButtons.withIndex()) {
-            if (index > 0) ButtonDivider()
-            ActionButton(actionButton)
+    if (isSpaExpressiveEnabled) {
+        Row(
+            horizontalArrangement = Arrangement.SpaceAround,
+            modifier = Modifier
+                .padding(SettingsDimension.buttonPadding)
+                .height(IntrinsicSize.Min)
+                .fillMaxWidth()
+        ) {
+            for (actionButton in actionButtons) {
+                ActionButton(actionButton)
+            }
+        }
+    } else {
+        Row(
+            Modifier
+                .padding(SettingsDimension.buttonPadding)
+                .clip(SettingsShape.CornerExtraLarge)
+                .height(IntrinsicSize.Min)
+        ) {
+            for ((index, actionButton) in actionButtons.withIndex()) {
+                if (index > 0) ButtonDivider()
+                ActionButton(actionButton)
+            }
         }
     }
 }
 
 @Composable
 private fun RowScope.ActionButton(actionButton: ActionButton) {
-    FilledTonalButton(
-        onClick = actionButton.onClick,
-        modifier = Modifier
-            .weight(1f)
-            .fillMaxHeight(),
-        enabled = actionButton.enabled,
-        // Because buttons could appear, disappear or change positions, reset the interaction source
-        // to prevent highlight the wrong button.
-        interactionSource = remember(actionButton) { MutableInteractionSource() },
-        shape = RectangleShape,
-        colors = ButtonDefaults.filledTonalButtonColors(
-            containerColor = MaterialTheme.colorScheme.surface,
-            contentColor = MaterialTheme.colorScheme.primary,
-            disabledContainerColor = MaterialTheme.colorScheme.surface,
-        ),
-        contentPadding = PaddingValues(horizontal = 4.dp, vertical = 20.dp),
-    ) {
+    if (isSpaExpressiveEnabled) {
         Column(horizontalAlignment = Alignment.CenterHorizontally) {
-            Icon(
-                imageVector = actionButton.imageVector,
-                contentDescription = null,
-                modifier = Modifier.size(SettingsDimension.itemIconSize),
-            )
+            FilledTonalButton(
+                onClick = actionButton.onClick,
+                modifier = Modifier
+                    .weight(1f)
+                    .fillMaxHeight()
+                    .clip(RoundedCornerShape(100.dp)),
+                enabled = actionButton.enabled,
+                // Because buttons could appear, disappear or change positions, reset the interaction source
+                // to prevent highlight the wrong button.
+                interactionSource = remember(actionButton) { MutableInteractionSource() },
+                shape = RectangleShape,
+                colors = ButtonDefaults.filledTonalButtonColors(
+                    containerColor = MaterialTheme.colorScheme.primaryContainer,
+                    contentColor = MaterialTheme.colorScheme.onPrimary,
+                    disabledContainerColor = MaterialTheme.colorScheme.surface,
+                ),
+                contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
+            ) {
+                Icon(
+                    imageVector = actionButton.imageVector,
+                    contentDescription = null,
+                    modifier = Modifier.size(SettingsDimension.itemIconSize),
+                )
+            }
             Box(
                 modifier = Modifier
-                    .padding(top = 4.dp)
-                    .fillMaxHeight(),
+                    .padding(top = 6.dp),
                 contentAlignment = Alignment.Center,
             ) {
                 Text(
@@ -113,6 +133,44 @@
                 )
             }
         }
+    } else {
+        FilledTonalButton(
+            onClick = actionButton.onClick,
+            modifier = Modifier
+                .weight(1f)
+                .fillMaxHeight(),
+            enabled = actionButton.enabled,
+            // Because buttons could appear, disappear or change positions, reset the interaction source
+            // to prevent highlight the wrong button.
+            interactionSource = remember(actionButton) { MutableInteractionSource() },
+            shape = RectangleShape,
+            colors = ButtonDefaults.filledTonalButtonColors(
+                containerColor = MaterialTheme.colorScheme.surface,
+                contentColor = MaterialTheme.colorScheme.primary,
+                disabledContainerColor = MaterialTheme.colorScheme.surface,
+            ),
+            contentPadding = PaddingValues(horizontal = 4.dp, vertical = 20.dp),
+        ) {
+            Column(horizontalAlignment = Alignment.CenterHorizontally) {
+                Icon(
+                    imageVector = actionButton.imageVector,
+                    contentDescription = null,
+                    modifier = Modifier.size(SettingsDimension.itemIconSize),
+                )
+                Box(
+                    modifier = Modifier
+                        .padding(top = 4.dp)
+                        .fillMaxHeight(),
+                    contentAlignment = Alignment.Center,
+                ) {
+                    Text(
+                        text = actionButton.text,
+                        textAlign = TextAlign.Center,
+                        style = MaterialTheme.typography.labelMedium,
+                    )
+                }
+            }
+        }
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
index 0898134..76df9c9 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
@@ -25,7 +25,6 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.RemoteException;
-import android.util.Log;
 import android.view.GestureDetector;
 import android.view.ISystemGestureExclusionListener;
 import android.view.IWindowManager;
@@ -76,10 +75,9 @@
  * touches are consumed.
  */
 public class TouchMonitor {
+    private final Logger mLogger;
     // This executor is used to protect {@code mActiveTouchSessions} from being modified
     // concurrently. Any operation that adds or removes values should use this executor.
-    public String TAG = "DreamOverlayTouchMonitor";
-    private final Logger mLogger;
     private final Executor mMainExecutor;
     private final Executor mBackgroundExecutor;
 
@@ -298,13 +296,12 @@
                     mWindowManagerService.registerSystemGestureExclusionListener(
                             mGestureExclusionListener, mDisplayId);
                 } catch (RemoteException e) {
-                    // Handle the exception
-                    Log.e(TAG, "Failed to register gesture exclusion listener", e);
+                    mLogger.e("Failed to register gesture exclusion listener", e);
                 }
             });
         }
         mCurrentInputSession = mInputSessionFactory.create(
-                        "dreamOverlay",
+                        mLoggingName,
                         mInputEventListener,
                         mOnGestureListener,
                         true)
@@ -326,7 +323,7 @@
                     }
                 } catch (RemoteException e) {
                     // Handle the exception
-                    Log.e(TAG, "unregisterSystemGestureExclusionListener: failed", e);
+                    mLogger.e("unregisterSystemGestureExclusionListener: failed", e);
                 }
             });
         }
@@ -543,6 +540,7 @@
     private InputSession mCurrentInputSession;
     private final int mDisplayId;
     private final IWindowManager mWindowManagerService;
+    private final String mLoggingName;
 
     private Rect mMaxBounds;
 
@@ -589,7 +587,8 @@
         mDisplayHelper = displayHelper;
         mWindowManagerService = windowManagerService;
         mConfigurationInteractor = configurationInteractor;
-        mLogger = new Logger(logBuffer, loggingName + ":TouchMonitor");
+        mLoggingName = loggingName + ":TouchMonitor";
+        mLogger = new Logger(logBuffer, mLoggingName);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 2e67277..52cb8d6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -289,6 +289,13 @@
         )
     }
 
+    private fun resetTouchMonitor() {
+        touchMonitor?.apply {
+            destroy()
+            touchMonitor = null
+        }
+    }
+
     /** Override for testing. */
     @VisibleForTesting
     internal fun initView(containerView: View): View {
@@ -297,12 +304,13 @@
             throw RuntimeException("Communal view has already been initialized")
         }
 
-        if (touchMonitor == null) {
-            touchMonitor =
-                ambientTouchComponentFactory.create(this, HashSet(), TAG).getTouchMonitor().apply {
-                    init()
-                }
-        }
+        resetTouchMonitor()
+
+        touchMonitor =
+            ambientTouchComponentFactory.create(this, HashSet(), TAG).getTouchMonitor().apply {
+                init()
+            }
+
         lifecycleRegistry.addObserver(touchLifecycleLogger)
         lifecycleRegistry.currentState = Lifecycle.State.CREATED
 
@@ -475,6 +483,8 @@
 
         lifecycleRegistry.removeObserver(touchLifecycleLogger)
 
+        resetTouchMonitor()
+
         logger.d("Hub container disposed")
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index b4a0f23..859f84e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -76,6 +76,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
@@ -717,6 +718,15 @@
             }
         }
 
+    @Test
+    fun disposeView_destroysTouchMonitor() {
+        clearInvocations(touchMonitor)
+
+        underTest.disposeView()
+
+        verify(touchMonitor).destroy()
+    }
+
     private fun initAndAttachContainerView() {
         val mockInsets =
             mock<WindowInsets> {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index aa57e0b..a19fddd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -68,11 +68,15 @@
 import com.android.internal.R;
 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Histogram;
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.Flags;
 import com.android.server.accessibility.gestures.GestureUtils;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * This class handles full screen magnification in response to touch events.
  *
@@ -871,6 +875,15 @@
      */
     class DetectingState implements State, Handler.Callback {
 
+        private static final Histogram HISTOGRAM_FIRST_INTERVAL =
+                new Histogram(
+                        "accessibility.value_full_triple_tap_first_interval",
+                        new Histogram.UniformOptions(25, 0, 250));
+        private static final Histogram HISTOGRAM_SECOND_INTERVAL =
+                new Histogram(
+                        "accessibility.value_full_triple_tap_second_interval",
+                        new Histogram.UniformOptions(25, 0, 250));
+
         private static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
         private static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
         private static final int MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE = 3;
@@ -1115,6 +1128,12 @@
             if (multitapTriggered && numTaps > 2) {
                 final boolean enabled = !isActivated();
                 mMagnificationLogger.logMagnificationTripleTap(enabled);
+
+                List<Long> intervals = intervalsOf(mDelayedEventQueue, ACTION_UP);
+                if (intervals.size() >= 2) {
+                    HISTOGRAM_FIRST_INTERVAL.logSample(intervals.get(0));
+                    HISTOGRAM_SECOND_INTERVAL.logSample(intervals.get(1));
+                }
             }
             return multitapTriggered;
         }
@@ -1144,6 +1163,10 @@
             return event != null ? event.getEventTime() : Long.MIN_VALUE;
         }
 
+        public List<Long> intervalsOf(MotionEventInfo info, int eventType) {
+            return MotionEventInfo.intervalsOf(info, eventType);
+        }
+
         public int tapCount() {
             return MotionEventInfo.countOf(mDelayedEventQueue, ACTION_UP);
         }
@@ -1649,7 +1672,7 @@
         return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(pointerDownLocation.y));
     }
 
-    private static final class MotionEventInfo {
+    public static final class MotionEventInfo {
 
         private static final int MAX_POOL_SIZE = 10;
         private static final Object sLock = new Object();
@@ -1709,6 +1732,14 @@
             }
         }
 
+        public MotionEventInfo getNext() {
+            return mNext;
+        }
+
+        public void setNext(MotionEventInfo info) {
+            mNext = info;
+        }
+
         private void clear() {
             event = recycleAndNullify(event);
             rawEvent = recycleAndNullify(rawEvent);
@@ -1721,6 +1752,23 @@
                     + countOf(info.mNext, eventType);
         }
 
+        static List<Long> intervalsOf(MotionEventInfo info, int eventType) {
+            List<Long> intervals = new ArrayList<>();
+            MotionEventInfo current = info;
+            MotionEventInfo previous = null;
+
+            while (current != null) {
+                if (current.event.getAction() == eventType) {
+                    if (previous != null) {
+                        intervals.add(current.event.getDownTime() - previous.event.getDownTime());
+                    }
+                    previous = current;
+                }
+                current = current.mNext;
+            }
+            return intervals;
+        }
+
         public static String toString(MotionEventInfo info) {
             return info == null
                     ? ""
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6af4be5..a6389f7 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -423,7 +423,7 @@
 
     /** Hands the definition of foreground and uid states */
     @GuardedBy("this")
-    public AppOpsUidStateTracker getUidStateTracker() {
+    private AppOpsUidStateTracker getUidStateTracker() {
         if (mUidStateTracker == null) {
             mUidStateTracker = new AppOpsUidStateTrackerImpl(
                     LocalServices.getService(ActivityManagerInternal.class),
@@ -2895,21 +2895,28 @@
                         uidState.uid, getPersistentId(virtualDeviceId), code);
 
                 if (rawUidMode != AppOpsManager.opToDefaultMode(code)) {
-                    return raw ? rawUidMode : uidState.evalMode(code, rawUidMode);
+                    return raw ? rawUidMode :
+                        evaluateForegroundMode(/* uid= */ uid, /* op= */ code,
+                        /* rawUidMode= */ rawUidMode);
                 }
             }
 
             Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false);
             if (op == null) {
-                return AppOpsManager.opToDefaultMode(code);
+                return evaluateForegroundMode(
+                        /* uid= */ uid,
+                        /* op= */ code,
+                        /* rawUidMode= */ AppOpsManager.opToDefaultMode(code));
             }
-            return raw
-                    ? mAppOpsCheckingService.getPackageMode(
-                            op.packageName, op.op, UserHandle.getUserId(op.uid))
-                    : op.uidState.evalMode(
-                            op.op,
-                            mAppOpsCheckingService.getPackageMode(
-                                    op.packageName, op.op, UserHandle.getUserId(op.uid)));
+            var packageMode = mAppOpsCheckingService.getPackageMode(
+                    op.packageName,
+                    op.op,
+                    UserHandle.getUserId(op.uid));
+            return raw ? packageMode :
+                    evaluateForegroundMode(
+                        /* uid= */ uid,
+                        /* op= */op.op,
+                        /* rawUidMode= */ packageMode);
         }
     }
 
@@ -7003,6 +7010,11 @@
                 "Requested persistentId for invalid virtualDeviceId: " + virtualDeviceId);
     }
 
+    @GuardedBy("this")
+    private int evaluateForegroundMode(int uid, int op, int rawUidMode) {
+        return getUidStateTracker().evalMode(uid, op, rawUidMode);
+    }
+
     private final class ClientUserRestrictionState implements DeathRecipient {
         private final IBinder token;
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 598d3a3..b745e6a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -32,6 +32,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -64,6 +65,7 @@
 import android.graphics.Region;
 import android.os.Handler;
 import android.os.Message;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -105,6 +107,7 @@
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.function.IntConsumer;
 
@@ -700,6 +703,15 @@
     }
 
     @Test
+    public void testIntervalsOf_sendMotionEventInfo_returnMatchIntervals() {
+        FullScreenMagnificationGestureHandler.MotionEventInfo upEventQueue =
+                createEventQueue(ACTION_UP, 0, 100, 300);
+
+        List<Long> upIntervals = mMgh.mDetectingState.intervalsOf(upEventQueue, ACTION_UP);
+        assertEquals(Arrays.asList(100L, 200L), upIntervals);
+    }
+
+    @Test
     public void testMagnifierDeactivates_shortcutTriggeredState_returnToIdleState() {
         goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
 
@@ -2294,6 +2306,31 @@
         return event;
     }
 
+    private FullScreenMagnificationGestureHandler.MotionEventInfo createEventQueue(
+            int eventType, long... delays) {
+        FullScreenMagnificationGestureHandler.MotionEventInfo eventQueue = null;
+        long currentTime = SystemClock.uptimeMillis();
+
+        for (int i = 0; i < delays.length; i++) {
+            MotionEvent event = MotionEvent.obtain(currentTime + delays[i],
+                    currentTime + delays[i], eventType, 0, 0, 0);
+
+            FullScreenMagnificationGestureHandler.MotionEventInfo info =
+                    FullScreenMagnificationGestureHandler.MotionEventInfo
+                    .obtain(event, MotionEvent.obtain(event), 0);
+
+            if (eventQueue == null) {
+                eventQueue = info;
+            } else {
+                FullScreenMagnificationGestureHandler.MotionEventInfo tail = eventQueue;
+                while (tail.getNext() != null) {
+                    tail = tail.getNext();
+                }
+                tail.setNext(info);
+            }
+        }
+        return eventQueue;
+    }
 
     private String stateDump() {
         return "\nCurrent state dump:\n" + mMgh + "\n" + mHandler.getPendingMessages();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2ef0573..47f6764 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9763,7 +9763,7 @@
      * users to switch to using satellite emergency messaging.</li>
      * </ul>
      * <p>
-     * The default value is 300 seconds.
+     * The default value is 180 seconds.
      */
     @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
     public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT =
@@ -11257,7 +11257,7 @@
                 KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
                 PersistableBundle.EMPTY);
         sDefaults.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false);
-        sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 300);
+        sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 180);
         sDefaults.putIntArray(KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,
                 // Boundaries: [-140 dBm, -44 dBm]
                 new int[]{