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 f757e1c88cb82f58c4d848e5057a57d6136cba3e..fb3c35b6a1e31eb45bf8f71ce39f54b100807a16 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
@@ -201,9 +201,11 @@ public abstract class WMShellModule {
     @Provides
     static WindowDecorViewModel provideWindowDecorViewModel(
             Context context,
+            @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler,
             @ShellMainThread Choreographer mainChoreographer,
             ShellInit shellInit,
+            IWindowManager windowManager,
             ShellCommandHandler shellCommandHandler,
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
@@ -216,10 +218,12 @@ public abstract class WMShellModule {
         if (DesktopModeStatus.isEnabled()) {
             return new DesktopModeWindowDecorViewModel(
                     context,
+                    mainExecutor,
                     mainHandler,
                     mainChoreographer,
                     shellInit,
                     shellCommandHandler,
+                    windowManager,
                     taskOrganizer,
                     displayController,
                     shellController,
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 96eaa1edbae477b8e201ecc385ab6a57feac3fac..91e9601c6a2768d1d16d8c5233f9f80b189106e4 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
@@ -190,6 +190,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
         mRelayoutParams.mShadowRadiusId = shadowRadiusID;
         mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
         mRelayoutParams.mSetTaskPositionAndCrop = setTaskCropAndPosition;
+        mRelayoutParams.mAllowCaptionInputFallthrough = false;
 
         relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
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 caa894fcbbc735a3f76cb6a5ec8dcd81e496908c..8d798a3035f636da6d2325903584ef3e627e5159 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
@@ -48,9 +48,13 @@ import android.graphics.Region;
 import android.hardware.input.InputManager;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.Choreographer;
 import android.view.GestureDetector;
+import android.view.ISystemGestureExclusionListener;
+import android.view.IWindowManager;
 import android.view.InputChannel;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
@@ -73,6 +77,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
@@ -88,6 +93,7 @@ import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener;
+import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 
 import java.io.PrintWriter;
 import java.util.Optional;
@@ -102,6 +108,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
     private static final String TAG = "DesktopModeWindowDecorViewModel";
 
     private final DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
+    private final IWindowManager mWindowManager;
+    private final ShellExecutor mMainExecutor;
     private final ActivityTaskManager mActivityTaskManager;
     private final ShellCommandHandler mShellCommandHandler;
     private final ShellTaskOrganizer mTaskOrganizer;
@@ -112,6 +120,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
     private final DisplayController mDisplayController;
     private final SyncTransactionQueue mSyncQueue;
     private final Optional<DesktopTasksController> mDesktopTasksController;
+    private final InputManager mInputManager;
+
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -135,14 +145,31 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
             new DesktopModeKeyguardChangeListener();
     private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
     private final DisplayInsetsController mDisplayInsetsController;
+    private final Region mExclusionRegion = Region.obtain();
     private boolean mInImmersiveMode;
 
+    private final ISystemGestureExclusionListener mGestureExclusionListener =
+            new ISystemGestureExclusionListener.Stub() {
+                @Override
+                public void onSystemGestureExclusionChanged(int displayId,
+                        Region systemGestureExclusion, Region systemGestureExclusionUnrestricted) {
+                    if (mContext.getDisplayId() != displayId) {
+                        return;
+                    }
+                    mMainExecutor.execute(() -> {
+                        mExclusionRegion.set(systemGestureExclusion);
+                    });
+                }
+            };
+
     public DesktopModeWindowDecorViewModel(
             Context context,
+            ShellExecutor shellExecutor,
             Handler mainHandler,
             Choreographer mainChoreographer,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
+            IWindowManager windowManager,
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
             ShellController shellController,
@@ -154,10 +181,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
     ) {
         this(
                 context,
+                shellExecutor,
                 mainHandler,
                 mainChoreographer,
                 shellInit,
                 shellCommandHandler,
+                windowManager,
                 taskOrganizer,
                 displayController,
                 shellController,
@@ -174,10 +203,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
     @VisibleForTesting
     DesktopModeWindowDecorViewModel(
             Context context,
+            ShellExecutor shellExecutor,
             Handler mainHandler,
             Choreographer mainChoreographer,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
+            IWindowManager windowManager,
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
             ShellController shellController,
@@ -190,6 +221,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
             Supplier<SurfaceControl.Transaction> transactionFactory,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
         mContext = context;
+        mMainExecutor = shellExecutor;
         mMainHandler = mainHandler;
         mMainChoreographer = mainChoreographer;
         mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
@@ -201,10 +233,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
         mTransitions = transitions;
         mDesktopTasksController = desktopTasksController;
         mShellCommandHandler = shellCommandHandler;
+        mWindowManager = windowManager;
         mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
         mInputMonitorFactory = inputMonitorFactory;
         mTransactionFactory = transactionFactory;
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+        mInputManager = mContext.getSystemService(InputManager.class);
 
         shellInit.addInitCallback(this::onInit, this);
     }
@@ -216,6 +250,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                 new DesktopModeOnInsetsChangedListener());
         mDesktopTasksController.ifPresent(c -> c.setOnTaskResizeAnimationListener(
                 new DeskopModeOnTaskResizeAnimationListener()));
+        try {
+            mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener,
+                    mContext.getDisplayId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to register window manager callbacks", e);
+        }
     }
 
     @Override
@@ -315,12 +355,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
             implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
             View.OnGenericMotionListener , DragDetector.MotionEventHandler {
         private static final int CLOSE_MAXIMIZE_MENU_DELAY_MS = 150;
+
         private final int mTaskId;
         private final WindowContainerToken mTaskToken;
         private final DragPositioningCallback mDragPositioningCallback;
         private final DragDetector mDragDetector;
         private final GestureDetector mGestureDetector;
 
+        /**
+         * Whether to pilfer the next motion event to send cancellations to the windows below.
+         * Useful when the caption window is spy and the gesture should be handle by the system
+         * instead of by the app for their custom header content.
+         */
+        private boolean mShouldPilferCaptionEvents;
         private boolean mIsDragging;
         private boolean mTouchscreenInUse;
         private boolean mHasLongClicked;
@@ -438,6 +485,40 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
             final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
             moveTaskToFront(decoration.mTaskInfo);
 
+            final int actionMasked = e.getActionMasked();
+            final boolean isDown = actionMasked == MotionEvent.ACTION_DOWN;
+            final boolean isUpOrCancel = actionMasked == MotionEvent.ACTION_CANCEL
+                    || actionMasked == MotionEvent.ACTION_UP;
+            if (isDown) {
+                final boolean downInCustomizableCaptionRegion =
+                        decoration.checkTouchEventInCustomizableRegion(e);
+                final boolean downInExclusionRegion = mExclusionRegion.contains(
+                        (int) e.getRawX(), (int) e.getRawY());
+                final boolean isTransparentCaption =
+                        TaskInfoKt.isTransparentCaptionBarAppearance(decoration.mTaskInfo);
+                // The caption window may be a spy window when the caption background is
+                // transparent, which means events will fall through to the app window. Make
+                // sure to cancel these events if they do not happen in the intersection of the
+                // customizable region and what the app reported as exclusion areas, because
+                // the drag-move or other caption gestures should take priority outside those
+                // regions.
+                mShouldPilferCaptionEvents = !(downInCustomizableCaptionRegion
+                        && downInExclusionRegion && isTransparentCaption);
+            }
+
+            if (!mShouldPilferCaptionEvents) {
+                // The event will be handled by a window below.
+                return false;
+            }
+            // Otherwise pilfer so that windows below receive cancellations for this gesture, and
+            // continue normal handling as a caption gesture.
+            if (mInputManager != null) {
+                mInputManager.pilferPointers(v.getViewRootImpl().getInputToken());
+            }
+            if (isUpOrCancel) {
+                // Gesture is finished, reset state.
+                mShouldPilferCaptionEvents = false;
+            }
             if (!mHasLongClicked && id != R.id.maximize_window) {
                 decoration.closeMaximizeMenuIfNeeded(e);
             }
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 74f460bf12263d2b568204d83c3dfec4d8639040..9e999ae52835b82a4fe90bec8346fa2522b254a1 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
@@ -56,6 +56,7 @@ import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder;
@@ -337,6 +338,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
             controlsElement.mWidthResId = R.dimen.desktop_mode_right_edge_buttons_width;
             controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END;
             relayoutParams.mOccludingCaptionElements.add(controlsElement);
+            relayoutParams.mAllowCaptionInputFallthrough =
+                    TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo);
         }
         if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ taskInfo.isFocused)) {
             relayoutParams.mShadowRadiusId = taskInfo.isFocused
@@ -698,6 +701,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
                 && inputPoint.y <= mResult.mCaptionHeight;
     }
 
+    /**
+     * Checks whether the touch event falls inside the customizable caption region.
+     */
+    boolean checkTouchEventInCustomizableRegion(MotionEvent ev) {
+        return mResult.mCustomizableCaptionRegion.contains((int) ev.getRawX(), (int) ev.getRawY());
+    }
+
     /**
      * Check a passed MotionEvent if a click has occurred on any button on this caption
      * Note this should only be called when a regular onClick is not possible
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 dc65855646eaa4c42d58152caf7c94a5a4876b26..32c2d1e9b257b2682b2bf65bb9d28b2f52188f21 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
@@ -31,6 +31,7 @@ import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.os.Binder;
 import android.view.Display;
 import android.view.InsetsSource;
@@ -311,6 +312,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
                 if (numOfElements == 0) {
                     boundingRects = null;
                 } else {
+                    // The customizable region can at most be equal to the caption bar.
+                    if (params.mAllowCaptionInputFallthrough) {
+                        outResult.mCustomizableCaptionRegion.set(mCaptionInsetsRect);
+                    }
                     boundingRects = new Rect[numOfElements];
                     for (int i = 0; i < numOfElements; i++) {
                         final OccludingCaptionElement element =
@@ -319,9 +324,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
                                 resources.getDimensionPixelSize(element.mWidthResId);
                         boundingRects[i] =
                                 calculateBoundingRect(element, elementWidthPx, mCaptionInsetsRect);
+                        // Subtract the regions used by the caption elements, the rest is
+                        // customizable.
+                        if (params.mAllowCaptionInputFallthrough) {
+                            outResult.mCustomizableCaptionRegion.op(boundingRects[i],
+                                    Region.Op.DIFFERENCE);
+                        }
                     }
                 }
-
                 // Add this caption as an inset source.
                 wct.addInsetsSource(mTaskInfo.token,
                         mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect,
@@ -389,6 +399,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
                         WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
         lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
         lp.setTrustedOverlay();
+        if (params.mAllowCaptionInputFallthrough) {
+            lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+        } else {
+            lp.inputFeatures &= ~WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+        }
         if (mViewHost == null) {
             mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
                     mCaptionWindowManager);
@@ -596,6 +611,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
         int mCaptionHeightId;
         int mCaptionWidthId;
         final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>();
+        boolean mAllowCaptionInputFallthrough;
 
         int mShadowRadiusId;
         int mCornerRadius;
@@ -610,6 +626,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
             mCaptionHeightId = Resources.ID_NULL;
             mCaptionWidthId = Resources.ID_NULL;
             mOccludingCaptionElements.clear();
+            mAllowCaptionInputFallthrough = false;
 
             mShadowRadiusId = Resources.ID_NULL;
             mCornerRadius = 0;
@@ -637,6 +654,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
         int mCaptionHeight;
         int mCaptionWidth;
         int mCaptionX;
+        final Region mCustomizableCaptionRegion = Region.obtain();
         int mWidth;
         int mHeight;
         T mRootView;
@@ -647,6 +665,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
             mCaptionHeight = 0;
             mCaptionWidth = 0;
             mCaptionX = 0;
+            mCustomizableCaptionRegion.setEmpty();
             mRootView = null;
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5dd96aceaec7c0193b528c92c6bd972e51211154
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.extension
+
+import android.app.TaskInfo
+import android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS
+import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
+
+val TaskInfo.isTransparentCaptionBarAppearance: Boolean
+    get() {
+        val appearance = taskDescription?.statusBarAppearance ?: 0
+        return (appearance and APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) != 0
+    }
+
+val TaskInfo.isLightCaptionBarAppearance: Boolean
+    get() {
+        val appearance = taskDescription?.statusBarAppearance ?: 0
+        return (appearance and APPEARANCE_LIGHT_CAPTION_BARS) != 0
+    }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index 7e5b9bd649f20309ce555a120acd9931ad29674c..b7dd01faa5430699ffd5eca7e4eafeb0254896d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -8,8 +8,6 @@ import android.graphics.Bitmap
 import android.graphics.Color
 import android.view.View
 import android.view.View.OnLongClickListener
-import android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS
-import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
 import android.widget.ImageButton
 import android.widget.ImageView
 import android.widget.TextView
@@ -22,6 +20,8 @@ import com.android.internal.R.attr.materialColorSurfaceContainerLow
 import com.android.internal.R.attr.materialColorSurfaceDim
 import com.android.wm.shell.R
 import com.android.wm.shell.windowdecor.MaximizeButtonView
+import com.android.wm.shell.windowdecor.extension.isLightCaptionBarAppearance
+import com.android.wm.shell.windowdecor.extension.isTransparentCaptionBarAppearance
 
 /**
  * A desktop mode window decoration used when the window is floating (i.e. freeform). It hosts
@@ -107,7 +107,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
 
     @ColorInt
     private fun getCaptionBackgroundColor(taskInfo: RunningTaskInfo): Int {
-        if (isTransparentBackgroundRequested(taskInfo)) {
+        if (taskInfo.isTransparentCaptionBarAppearance) {
             return Color.TRANSPARENT
         }
         val materialColorAttr: Int =
@@ -133,10 +133,10 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
     @ColorInt
     private fun getAppNameAndButtonColor(taskInfo: RunningTaskInfo): Int {
         val materialColorAttr = when {
-            isTransparentBackgroundRequested(taskInfo) &&
-                    isLightCaptionBar(taskInfo) -> materialColorOnSecondaryContainer
-            isTransparentBackgroundRequested(taskInfo) &&
-                    !isLightCaptionBar(taskInfo) -> materialColorOnSurface
+            taskInfo.isTransparentCaptionBarAppearance &&
+                    taskInfo.isLightCaptionBarAppearance -> materialColorOnSecondaryContainer
+            taskInfo.isTransparentCaptionBarAppearance &&
+                    !taskInfo.isLightCaptionBarAppearance -> materialColorOnSurface
             isDarkMode() -> materialColorOnSurface
             else -> materialColorOnSecondaryContainer
         }
@@ -167,16 +167,6 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
                 Configuration.UI_MODE_NIGHT_YES
     }
 
-    private fun isTransparentBackgroundRequested(taskInfo: RunningTaskInfo): Boolean {
-        val appearance = taskInfo.taskDescription?.statusBarAppearance ?: 0
-        return (appearance and APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) != 0
-    }
-
-    private fun isLightCaptionBar(taskInfo: RunningTaskInfo): Boolean {
-        val appearance = taskInfo.taskDescription?.statusBarAppearance ?: 0
-        return (appearance and APPEARANCE_LIGHT_CAPTION_BARS) != 0
-    }
-
     companion object {
         private const val TAG = "DesktopModeAppControlsWindowDecorationViewHolder"
         private const val DARK_THEME_UNFOCUSED_OPACITY = 140 // 55%
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 f84685a92b57fa639d91088284cc5f04dc04c95d..917fd715f71ff2f29191ababc72a15cc17e2ee93 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
@@ -31,6 +31,7 @@ import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.view.Choreographer
 import android.view.Display.DEFAULT_DISPLAY
+import android.view.IWindowManager
 import android.view.InputChannel
 import android.view.InputMonitor
 import android.view.InsetsSource
@@ -96,6 +97,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
     @Mock private lateinit var mockShellExecutor: ShellExecutor
     @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
     @Mock private lateinit var mockShellCommandHandler: ShellCommandHandler
+    @Mock private lateinit var mockWindowManager: IWindowManager
 
     private val transactionFactory = Supplier<SurfaceControl.Transaction> {
         SurfaceControl.Transaction()
@@ -110,10 +112,12 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
         shellInit = ShellInit(mockShellExecutor)
         desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
                 mContext,
+                mockShellExecutor,
                 mockMainHandler,
                 mockMainChoreographer,
                 shellInit,
                 mockShellCommandHandler,
+                mockWindowManager,
                 mockTaskOrganizer,
                 mockDisplayController,
                 mockShellController,
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 40e61dd95f51bb382d39694607f1ad462bb30a2d..9e62bd254ac548f0289fd5df0be2ec4e67208ad4 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
@@ -16,6 +16,10 @@
 
 package com.android.wm.shell.windowdecor;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.any;
@@ -168,6 +172,57 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
         assertThat(relayoutParams.mCornerRadius).isGreaterThan(0);
     }
 
+    @Test
+    public void updateRelayoutParams_freeformAndTransparent_allowsInputFallthrough() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        taskInfo.taskDescription.setStatusBarAppearance(
+                APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false);
+
+        assertThat(relayoutParams.mAllowCaptionInputFallthrough).isTrue();
+    }
+
+    @Test
+    public void updateRelayoutParams_freeformButOpaque_disallowsInputFallthrough() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        taskInfo.taskDescription.setStatusBarAppearance(0);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false);
+
+        assertThat(relayoutParams.mAllowCaptionInputFallthrough).isFalse();
+    }
+
+    @Test
+    public void updateRelayoutParams_fullscreen_disallowsInputFallthrough() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false);
+
+        assertThat(relayoutParams.mAllowCaptionInputFallthrough).isFalse();
+    }
+
     private void fillRoundedCornersResources(int fillValue) {
         when(mMockRoundedCornersRadiusArray.getDimensionPixelSize(anyInt(), anyInt()))
                 .thenReturn(fillValue);