Merge changes from topic "am-983c3c67c6104e739b86553d061025dd" into main

* changes:
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 67e78fe002 -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: edfccdffef -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 0f657fce42 -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: b75a2f9426 -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 6c6deb6a5a -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 72659ffa58 -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 6b9a9116c4 -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 90fb4a3d6c -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 0a2f08e35d -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: c751242dc5 -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: d5c148dc3b -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: d9337dbeee -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 1865347417 -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: e55ce378ea -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 63d6a0243c -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: b7fd25133d -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: b2949a9ac3 -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: a1a0333ac2 -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: d7fef212ee -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: e35fa37b46 -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 06c22c478e -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 96836aa3a8 -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: ff77eedede -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: cb89a261d9 -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 5f3659caa8 -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 3ddd21d824 -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: ac9bf27e79 -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: a91f0ca6cc -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 645f04b2bd -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: b9d45627c9 -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: d83624ef23 -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: edf040e0a3 -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 3db3b38e3a -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 2d7d29bd6d -s ours
  [automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: de2079f8ea -s ours
  [automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: c4b85abb8e -s ours
diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt
index 5178f09..5efda98 100644
--- a/api/coverage/tools/ExtractFlaggedApis.kt
+++ b/api/coverage/tools/ExtractFlaggedApis.kt
@@ -17,6 +17,7 @@
 package android.platform.coverage
 
 import com.android.tools.metalava.model.ClassItem
+import com.android.tools.metalava.model.Item
 import com.android.tools.metalava.model.MethodItem
 import com.android.tools.metalava.model.text.ApiFile
 import java.io.File
@@ -44,20 +45,9 @@
     builder: FlagApiMap.Builder
 ) {
     if (methods.isEmpty()) return
-    val classFlag =
-        classItem.modifiers
-            .findAnnotation("android.annotation.FlaggedApi")
-            ?.findAttribute("value")
-            ?.value
-            ?.value() as? String
+    val classFlag = getClassFlag(classItem)
     for (method in methods) {
-        val methodFlag =
-            method.modifiers
-                .findAnnotation("android.annotation.FlaggedApi")
-                ?.findAttribute("value")
-                ?.value
-                ?.value() as? String
-                ?: classFlag
+        val methodFlag = getFlagAnnotation(method) ?: classFlag
         val api =
             JavaMethod.newBuilder()
                 .setPackageName(packageName)
@@ -81,3 +71,23 @@
         builder.putFlagToApi(flag, apis)
     }
 }
+
+fun getClassFlag(classItem: ClassItem): String? {
+    var classFlag = getFlagAnnotation(classItem)
+    var cur = classItem
+    // If a class is not an inner class, use its @FlaggedApi annotation value.
+    // Otherwise, use the flag value of the closest outer class that is annotated by @FlaggedApi.
+    while (cur.isInnerClass() && classFlag == null) {
+        cur = cur.parent() as ClassItem
+        classFlag = getFlagAnnotation(cur)
+    }
+    return classFlag
+}
+
+fun getFlagAnnotation(item: Item): String? {
+    return item.modifiers
+        .findAnnotation("android.annotation.FlaggedApi")
+        ?.findAttribute("value")
+        ?.value
+        ?.value() as? String
+}
diff --git a/api/coverage/tools/ExtractFlaggedApisTest.kt b/api/coverage/tools/ExtractFlaggedApisTest.kt
index ee5aaf1..427be36 100644
--- a/api/coverage/tools/ExtractFlaggedApisTest.kt
+++ b/api/coverage/tools/ExtractFlaggedApisTest.kt
@@ -141,6 +141,84 @@
         assertThat(result).isEqualTo(expected.build())
     }
 
+    @Test
+    fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag() {
+        val apiText =
+            """
+            // Signature format: 2.0
+            package android.location.provider {
+              @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ForwardGeocodeRequest implements android.os.Parcelable {
+                method public int describeContents();
+              }
+              public static final class ForwardGeocodeRequest.Builder {
+                method @NonNull public android.location.provider.ForwardGeocodeRequest build();
+              }
+            }
+        """
+                .trimIndent()
+        Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND)
+
+        val process = Runtime.getRuntime().exec(createCommand())
+        process.waitFor()
+
+        val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8)
+        val result = TextFormat.parse(content, FlagApiMap::class.java)
+
+        val expected = FlagApiMap.newBuilder()
+        val api1 =
+            JavaMethod.newBuilder()
+                .setPackageName("android.location.provider")
+                .setClassName("ForwardGeocodeRequest")
+                .setMethodName("describeContents")
+        addFlaggedApi(expected, api1, "Flags.FLAG_NEW_GEOCODER")
+        val api2 =
+            JavaMethod.newBuilder()
+                .setPackageName("android.location.provider")
+                .setClassName("ForwardGeocodeRequest.Builder")
+                .setMethodName("build")
+        addFlaggedApi(expected, api2, "Flags.FLAG_NEW_GEOCODER")
+        assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build())
+    }
+
+    @Test
+    fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag_deeplyNested() {
+        val apiText =
+            """
+            // Signature format: 2.0
+            package android.package.xyz {
+              @FlaggedApi(outer_class_flag) public final class OuterClass {
+                method public int apiInOuterClass();
+              }
+              public final class OuterClass.Deeply.NestedClass {
+                method public void apiInNestedClass();
+              }
+            }
+        """
+                .trimIndent()
+        Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND)
+
+        val process = Runtime.getRuntime().exec(createCommand())
+        process.waitFor()
+
+        val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8)
+        val result = TextFormat.parse(content, FlagApiMap::class.java)
+
+        val expected = FlagApiMap.newBuilder()
+        val api1 =
+            JavaMethod.newBuilder()
+                .setPackageName("android.package.xyz")
+                .setClassName("OuterClass")
+                .setMethodName("apiInOuterClass")
+        addFlaggedApi(expected, api1, "outer_class_flag")
+        val api2 =
+            JavaMethod.newBuilder()
+                .setPackageName("android.package.xyz")
+                .setClassName("OuterClass.Deeply.NestedClass")
+                .setMethodName("apiInNestedClass")
+        addFlaggedApi(expected, api2, "outer_class_flag")
+        assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build())
+    }
+
     private fun addFlaggedApi(builder: FlagApiMap.Builder, api: JavaMethod.Builder, flag: String) {
         if (builder.containsFlagToApi(flag)) {
             val updatedApis =
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index b52003f..1f6ceca 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -22,7 +22,6 @@
 import static android.view.InsetsControllerProto.STATE;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.InsetsSource.ID_IME_CAPTION_BAR;
-import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
 import static android.view.WindowInsets.Type.FIRST;
 import static android.view.WindowInsets.Type.LAST;
 import static android.view.WindowInsets.Type.all;
@@ -685,9 +684,6 @@
 
                 @Override
                 public void onIdNotFoundInState2(int index1, InsetsSource source1) {
-                    if (!CAPTION_ON_SHELL && source1.getType() == captionBar()) {
-                        return;
-                    }
                     if (source1.getId() == ID_IME_CAPTION_BAR) {
                         return;
                     }
@@ -848,15 +844,8 @@
     }
 
     public boolean onStateChanged(InsetsState state) {
-        boolean stateChanged = false;
-        if (!CAPTION_ON_SHELL) {
-            stateChanged = !mState.equals(state, true /* excludesCaptionBar */,
-                    false /* excludesInvisibleIme */)
-                    || captionInsetsUnchanged();
-        } else {
-            stateChanged = !mState.equals(state, false /* excludesCaptionBar */,
-                    false /* excludesInvisibleIme */);
-        }
+        boolean stateChanged = !mState.equals(state, false /* excludesCaptionBar */,
+                false /* excludesInvisibleIme */);
         if (!stateChanged && mLastDispatchedState.equals(state)) {
             return false;
         }
@@ -924,21 +913,6 @@
         }
     }
 
-    private boolean captionInsetsUnchanged() {
-        if (CAPTION_ON_SHELL) {
-            return false;
-        }
-        final InsetsSource source = mState.peekSource(ID_CAPTION_BAR);
-        if (source == null && mCaptionInsetsHeight == 0) {
-            return false;
-        }
-        if (source != null && mCaptionInsetsHeight == source.getFrame().height()) {
-            return false;
-        }
-
-        return true;
-    }
-
     /**
      * @see InsetsState#calculateInsets(Rect, InsetsState, boolean, int, int, int, int, int,
      *      android.util.SparseIntArray)
@@ -1889,24 +1863,6 @@
     }
 
     @Override
-    public void setCaptionInsetsHeight(int height) {
-        // This method is to be removed once the caption is moved to the shell.
-        if (CAPTION_ON_SHELL) {
-            return;
-        }
-        if (mCaptionInsetsHeight != height) {
-            mCaptionInsetsHeight = height;
-            if (mCaptionInsetsHeight != 0) {
-                mState.getOrCreateSource(ID_CAPTION_BAR, captionBar()).setFrame(
-                        mFrame.left, mFrame.top, mFrame.right, mFrame.top + mCaptionInsetsHeight);
-            } else {
-                mState.removeSource(ID_CAPTION_BAR);
-            }
-            mHost.notifyInsetsChanged();
-        }
-    }
-
-    @Override
     public void setImeCaptionBarInsetsHeight(int height) {
         if (!ENABLE_HIDE_IME_CAPTION_BAR) {
             return;
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index 00a5806..5f461c5 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -45,7 +45,6 @@
     private InsetsController mReplayedInsetsController;
     private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
             = new ArrayList<>();
-    private int mCaptionInsetsHeight = 0;
     private int mImeCaptionBarInsetsHeight = 0;
     private WindowInsetsAnimationControlListener mLoggingListener;
     private @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();
@@ -99,11 +98,6 @@
     }
 
     @Override
-    public void setCaptionInsetsHeight(int height) {
-        mCaptionInsetsHeight = height;
-    }
-
-    @Override
     public void setImeCaptionBarInsetsHeight(int height) {
         mImeCaptionBarInsetsHeight = height;
     }
@@ -187,9 +181,6 @@
             controller.setSystemBarsAppearanceFromResource(
                     mAppearanceFromResource, mAppearanceFromResourceMask);
         }
-        if (mCaptionInsetsHeight != 0) {
-            controller.setCaptionInsetsHeight(mCaptionInsetsHeight);
-        }
         if (mImeCaptionBarInsetsHeight != 0) {
             controller.setImeCaptionBarInsetsHeight(mImeCaptionBarInsetsHeight);
         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f039a19..cd0602c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -257,7 +257,6 @@
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.SomeArgs;
-import com.android.internal.policy.DecorView;
 import com.android.internal.policy.PhoneFallbackEventHandler;
 import com.android.internal.view.BaseSurfaceHolder;
 import com.android.internal.view.RootViewSurfaceTaker;
@@ -335,13 +334,6 @@
     private static final boolean USE_ASYNC_PERFORM_HAPTIC_FEEDBACK = true;
 
     /**
-     * Whether the caption is drawn by the shell.
-     * @hide
-     */
-    public static final boolean CAPTION_ON_SHELL =
-            SystemProperties.getBoolean("persist.wm.debug.caption_on_shell", true);
-
-    /**
      * Whether the client (system UI) is handling the transient gesture and the corresponding
      * animation.
      * @hide
@@ -3176,22 +3168,6 @@
         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
 
-    private boolean updateCaptionInsets() {
-        if (CAPTION_ON_SHELL) {
-            return false;
-        }
-        if (!(mView instanceof DecorView)) return false;
-        final int captionInsetsHeight = ((DecorView) mView).getCaptionInsetsHeight();
-        final Rect captionFrame = new Rect();
-        if (captionInsetsHeight != 0) {
-            captionFrame.set(mWinFrame.left, mWinFrame.top, mWinFrame.right,
-                            mWinFrame.top + captionInsetsHeight);
-        }
-        if (mAttachInfo.mCaptionInsets.equals(captionFrame)) return false;
-        mAttachInfo.mCaptionInsets.set(captionFrame);
-        return true;
-    }
-
     private boolean shouldDispatchCutout() {
         return mWindowAttributes.layoutInDisplayCutoutMode
                         == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
@@ -3645,9 +3621,6 @@
                     mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars;
                     dispatchApplyInsets = true;
                 }
-                if (updateCaptionInsets()) {
-                    dispatchApplyInsets = true;
-                }
                 if (dispatchApplyInsets || mLastSystemUiVisibility !=
                         mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) {
                     mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 1ffffb3..19c98a2 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -284,15 +284,6 @@
     @Appearance int getSystemBarsAppearance();
 
     /**
-     * Notify the caption insets height change. The information will be used on the client side to,
-     * make sure the InsetsState has the correct caption insets.
-     *
-     * @param height the height of caption bar insets.
-     * @hide
-     */
-    void setCaptionInsetsHeight(int height);
-
-    /**
      * Sets the insets height for the IME caption bar, which corresponds to the
      * "fake" IME navigation bar.
      *
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 6848646..c6e8bf7 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -56,7 +56,6 @@
     // during a configuration change.
     private int mLastContentWidth;
     private int mLastContentHeight;
-    private int mLastCaptionHeight;
     private int mLastXOffset;
     private int mLastYOffset;
 
@@ -269,7 +268,7 @@
             final boolean firstCall = mLastContentWidth == 0;
             // The current content buffer is drawn here.
             mLastContentWidth = xSize;
-            mLastContentHeight = ySize - mLastCaptionHeight;
+            mLastContentHeight = ySize;
             mLastXOffset = xOffset;
             mLastYOffset = yOffset;
 
@@ -278,12 +277,11 @@
                     mLastXOffset,
                     mLastYOffset,
                     mLastXOffset + mLastContentWidth,
-                    mLastYOffset + mLastCaptionHeight + mLastContentHeight);
+                    mLastYOffset + mLastContentHeight);
 
             // If this was the first call and redrawLocked got already called prior
             // to us, we should re-issue a redrawLocked now.
-            return firstCall
-                    && (mLastCaptionHeight != 0 || !mDecorView.isShowingCaption());
+            return firstCall;
         }
     }
 
@@ -303,22 +301,9 @@
      */
     private void redrawLocked(Rect newBounds, boolean fullscreen) {
 
-        // While a configuration change is taking place the view hierarchy might become
-        // inaccessible. For that case we remember the previous metrics to avoid flashes.
-        // Note that even when there is no visible caption, the caption child will exist.
-        final int captionHeight = mDecorView.getCaptionHeight();
-
-        // The caption height will probably never dynamically change while we are resizing.
-        // Once set to something other then 0 it should be kept that way.
-        if (captionHeight != 0) {
-            // Remember the height of the caption.
-            mLastCaptionHeight = captionHeight;
-        }
-
         // Make sure that the other thread has already prepared the render draw calls for the
         // content. If any size is 0, we have to wait for it to be drawn first.
-        if ((mLastCaptionHeight == 0 && mDecorView.isShowingCaption()) ||
-                mLastContentWidth == 0 || mLastContentHeight == 0) {
+        if (mLastContentWidth == 0 || mLastContentHeight == 0) {
             return;
         }
 
@@ -337,13 +322,13 @@
                 ? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable;
 
         if (drawable != null) {
-            drawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
+            drawable.setBounds(0, 0, left + width, top);
             drawable.draw(canvas);
         }
 
         // The backdrop: clear everything with the background. Clipping is done elsewhere.
         if (mResizingBackgroundDrawable != null) {
-            mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
+            mResizingBackgroundDrawable.setBounds(0, 0, left + width, top + height);
             mResizingBackgroundDrawable.draw(canvas);
         }
         mFrameAndBackdropNode.endRecording();
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index a65a1bb..ab37252 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -26,9 +26,6 @@
 import static android.view.View.MeasureSpec.getMode;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
-import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
-import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
 import static android.view.WindowInsetsController.APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -38,9 +35,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
 import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
@@ -83,7 +77,6 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.PendingInsetsController;
-import android.view.ThreadedRenderer;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
@@ -116,7 +109,6 @@
 import com.android.internal.view.menu.MenuHelper;
 import com.android.internal.widget.ActionBarContextView;
 import com.android.internal.widget.BackgroundFallback;
-import com.android.internal.widget.DecorCaptionView;
 import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
 
 import java.util.List;
@@ -189,8 +181,6 @@
 
     private final Rect mFrameOffsets = new Rect();
 
-    private boolean mHasCaption = false;
-
     private boolean mChanging;
 
     private Drawable mMenuBackground;
@@ -247,11 +237,6 @@
 
     private Rect mTempRect;
 
-    // This is the caption view for the window, containing the caption and window control
-    // buttons. The visibility of this decor depends on the workspace and the window type.
-    // If the window type does not require such a view, this member might be null.
-    private DecorCaptionView mDecorCaptionView;
-
     private boolean mWindowResizeCallbacksAdded = false;
     private Drawable.Callback mLastBackgroundDrawableCb = null;
     private BackdropFrameRenderer mBackdropFrameRenderer = null;
@@ -524,24 +509,6 @@
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
         int action = event.getAction();
-        if (mHasCaption && isShowingCaption()) {
-            // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event
-            // was (starting) outside the window. Window resizing events should be handled by
-            // WindowManager.
-            // TODO: Investigate how to handle the outside touch in window manager
-            //       without generating these events.
-            //       Currently we receive these because we need to enlarge the window's
-            //       touch region so that the monitor channel receives the events
-            //       in the outside touch area.
-            if (action == MotionEvent.ACTION_DOWN) {
-                final int x = (int) event.getX();
-                final int y = (int) event.getY();
-                if (isOutOfInnerBounds(x, y)) {
-                    return true;
-                }
-            }
-        }
-
         if (mFeatureId >= 0) {
             if (action == MotionEvent.ACTION_DOWN) {
                 int x = (int)event.getX();
@@ -1041,7 +1008,6 @@
     @Override
     public void onWindowSystemUiVisibilityChanged(int visible) {
         updateColorViews(null /* insets */, true /* animate */);
-        updateDecorCaptionStatus(getResources().getConfiguration());
 
         if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
             updateStatusGuardColor();
@@ -1214,11 +1180,6 @@
                     mLastTopInset, false /* matchVertical */, statusBarNeedsLeftInset,
                     statusBarSideInset, animate && !disallowAnimate,
                     mForceWindowDrawsBarBackgrounds, requestedVisibleTypes);
-
-            if (mHasCaption) {
-                mDecorCaptionView.getCaption().setBackgroundColor(statusBarColor);
-                updateDecorCaptionShade();
-            }
         }
 
         // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
@@ -1483,7 +1444,7 @@
                 mWindow.getAttributes().flags, force);
         boolean show = state.attributes.isVisible(state.present, color,
                 mWindow.getAttributes().flags, force);
-        boolean showView = show && !isResizing() && !mHasCaption && size > 0;
+        boolean showView = show && !isResizing() && size > 0;
 
         boolean visibilityChanged = false;
         View view = state.view;
@@ -1950,9 +1911,6 @@
     @Override
     public void onRootViewScrollYChanged(int rootScrollY) {
         mRootScrollY = rootScrollY;
-        if (mDecorCaptionView != null) {
-            mDecorCaptionView.onRootViewScrollYChanged(rootScrollY);
-        }
         updateColorViewTranslations();
     }
 
@@ -2141,31 +2099,6 @@
             .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
     }
 
-    /**
-     * Informs the decor if the caption is attached and visible.
-     * @param attachedAndVisible true when the decor is visible.
-     * Note that this will even be called if there is no caption.
-     **/
-    void enableCaption(boolean attachedAndVisible) {
-        if (mHasCaption != attachedAndVisible) {
-            mHasCaption = attachedAndVisible;
-            if (getForeground() != null) {
-                drawableChanged();
-            }
-            notifyCaptionHeightChanged();
-        }
-    }
-
-    /**
-     * An interface to be called when the caption visibility or height changed, to report the
-     * corresponding insets change to the InsetsController.
-     */
-    public void notifyCaptionHeightChanged() {
-        if (!CAPTION_ON_SHELL) {
-            getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight());
-        }
-    }
-
     void setWindow(PhoneWindow phoneWindow) {
         mWindow = phoneWindow;
         Context context = getContext();
@@ -2191,8 +2124,6 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
-        updateDecorCaptionStatus(newConfig);
-
         initializeElevation();
     }
 
@@ -2214,29 +2145,6 @@
                 & View.SYSTEM_UI_FLAG_FULLSCREEN));
     }
 
-    private void updateDecorCaptionStatus(Configuration config) {
-        final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption()
-                && !isFillingScreen(config);
-        if (mDecorCaptionView == null && displayWindowDecor) {
-            // Configuration now requires a caption.
-            final LayoutInflater inflater = mWindow.getLayoutInflater();
-            mDecorCaptionView = createDecorCaptionView(inflater);
-            if (mDecorCaptionView != null) {
-                if (mDecorCaptionView.getParent() == null) {
-                    addView(mDecorCaptionView, 0,
-                            new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
-                }
-                removeView(mContentRoot);
-                mDecorCaptionView.addView(mContentRoot,
-                        new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
-            }
-        } else if (mDecorCaptionView != null) {
-            // We might have to change the kind of surface before we do anything else.
-            mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
-            enableCaption(displayWindowDecor);
-        }
-    }
-
     void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
         if (mBackdropFrameRenderer != null) {
             loadBackgroundDrawablesIfNeeded();
@@ -2246,20 +2154,10 @@
                     getCurrentColor(mNavigationColorViewState));
         }
 
-        mDecorCaptionView = createDecorCaptionView(inflater);
         final View root = inflater.inflate(layoutResource, null);
-        if (mDecorCaptionView != null) {
-            if (mDecorCaptionView.getParent() == null) {
-                addView(mDecorCaptionView,
-                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
-            }
-            mDecorCaptionView.addView(root,
-                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
-        } else {
 
-            // Put it below the color views.
-            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
-        }
+        // Put it below the color views.
+        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
         mContentRoot = (ViewGroup) root;
         initializeElevation();
     }
@@ -2285,89 +2183,6 @@
         }
     }
 
-    // Free floating overlapping windows require a caption.
-    private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
-        DecorCaptionView decorCaptionView = null;
-        for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
-            View view = getChildAt(i);
-            if (view instanceof DecorCaptionView) {
-                // The decor was most likely saved from a relaunch - so reuse it.
-                decorCaptionView = (DecorCaptionView) view;
-                removeViewAt(i);
-            }
-        }
-        final WindowManager.LayoutParams attrs = mWindow.getAttributes();
-        final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
-                attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
-        final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
-        // Only a non floating application window on one of the allowed workspaces can get a caption
-        if (!mWindow.isFloating() && isApplication && winConfig.hasWindowDecorCaption()
-                && !CAPTION_ON_SHELL) {
-            // Dependent on the brightness of the used title we either use the
-            // dark or the light button frame.
-            if (decorCaptionView == null) {
-                decorCaptionView = inflateDecorCaptionView(inflater);
-            }
-            decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
-        } else {
-            decorCaptionView = null;
-        }
-
-        // Tell the decor if it has a visible caption.
-        enableCaption(decorCaptionView != null);
-        return decorCaptionView;
-    }
-
-    private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
-        final Context context = getContext();
-        // We make a copy of the inflater, so it has the right context associated with it.
-        inflater = inflater.from(context);
-        final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
-                null);
-        setDecorCaptionShade(view);
-        return view;
-    }
-
-    private void setDecorCaptionShade(DecorCaptionView view) {
-        final int shade = mWindow.getDecorCaptionShade();
-        switch (shade) {
-            case DECOR_CAPTION_SHADE_LIGHT:
-                setLightDecorCaptionShade(view);
-                break;
-            case DECOR_CAPTION_SHADE_DARK:
-                setDarkDecorCaptionShade(view);
-                break;
-            default: {
-                if ((getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0) {
-                    setDarkDecorCaptionShade(view);
-                } else {
-                    setLightDecorCaptionShade(view);
-                }
-                break;
-            }
-        }
-    }
-
-    void updateDecorCaptionShade() {
-        if (mDecorCaptionView != null) {
-            setDecorCaptionShade(mDecorCaptionView);
-        }
-    }
-
-    private void setLightDecorCaptionShade(DecorCaptionView view) {
-        view.findViewById(R.id.maximize_window).setBackgroundResource(
-                R.drawable.decor_maximize_button_light);
-        view.findViewById(R.id.close_window).setBackgroundResource(
-                R.drawable.decor_close_button_light);
-    }
-
-    private void setDarkDecorCaptionShade(DecorCaptionView view) {
-        view.findViewById(R.id.maximize_window).setBackgroundResource(
-                R.drawable.decor_maximize_button_dark);
-        view.findViewById(R.id.close_window).setBackgroundResource(
-                R.drawable.decor_close_button_dark);
-    }
-
     /**
      * Returns the color used to fill areas the app has not rendered content to yet when the
      * user is resizing the window of an activity in multi-window mode.
@@ -2405,17 +2220,11 @@
     }
 
     void clearContentView() {
-        if (mDecorCaptionView != null) {
-            mDecorCaptionView.removeContentView();
-        } else {
-            // This window doesn't have caption, so we need to remove everything except our views
-            // we might have added.
-            for (int i = getChildCount() - 1; i >= 0; i--) {
-                View v = getChildAt(i);
-                if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
-                        && v != mStatusGuard) {
-                    removeViewAt(i);
-                }
+        for (int i = getChildCount() - 1; i >= 0; i--) {
+            View v = getChildAt(i);
+            if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
+                    && v != mStatusGuard) {
+                removeViewAt(i);
             }
         }
     }
@@ -2439,23 +2248,6 @@
         if (mBackdropFrameRenderer != null) {
             return;
         }
-        final ThreadedRenderer renderer = getThreadedRenderer();
-        if (renderer != null && !CAPTION_ON_SHELL) {
-            loadBackgroundDrawablesIfNeeded();
-            WindowInsets rootInsets = getRootWindowInsets();
-            mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
-                    initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
-                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
-                    getCurrentColor(mNavigationColorViewState), fullscreen,
-                    rootInsets.getInsets(WindowInsets.Type.systemBars()));
-
-            // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
-            // If we want to get the shadow shown while resizing, we would need to elevate a new
-            // element which owns the caption and has the elevation.
-            updateElevation();
-
-            updateColorViews(null /* insets */, false);
-        }
         getViewRootImpl().requestInvalidateRootRenderNode();
     }
 
@@ -2576,23 +2368,6 @@
         }
     }
 
-    boolean isShowingCaption() {
-        return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
-    }
-
-    int getCaptionHeight() {
-        return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
-    }
-
-    /**
-     * @hide
-     * @return the height of insets covering the top of window content area.
-     */
-    public int getCaptionInsetsHeight() {
-        if (!mWindow.isOverlayWithDecorCaptionEnabled()) return 0;
-        return getCaptionHeight();
-    }
-
     /**
      * Converts a DIP measure into physical pixels.
      * @param dip The dip value.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index e6a2a6c..1841353 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -363,8 +363,6 @@
     private boolean mIsStartingWindow;
     private int mTheme = -1;
 
-    private int mDecorCaptionShade = DECOR_CAPTION_SHADE_AUTO;
-
     private boolean mUseDecorContext = false;
 
     /** @see ViewRootImpl#mActivityConfigCallback */
@@ -4048,14 +4046,7 @@
 
     @Override
     public void setDecorCaptionShade(int decorCaptionShade) {
-        mDecorCaptionShade = decorCaptionShade;
-        if (mDecor != null) {
-            mDecor.updateDecorCaptionShade();
-        }
-    }
-
-    int getDecorCaptionShade() {
-        return mDecorCaptionShade;
+        // TODO(b/328668781): Make proper treatment to this public API per the outcome of the bug.
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
deleted file mode 100644
index 362fd7b..0000000
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Copyright (C) 2015 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.internal.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.Window;
-
-import com.android.internal.R;
-import com.android.internal.policy.DecorView;
-import com.android.internal.policy.PhoneWindow;
-
-import java.util.ArrayList;
-
-/**
- * This class represents the special screen elements to control a window on freeform
- * environment.
- * As such this class handles the following things:
- * <ul>
- * <li>The caption, containing the system buttons like maximize, close and such as well as
- * allowing the user to drag the window around.</li>
- * </ul>
- * After creating the view, the function {@link #setPhoneWindow} needs to be called to make
- * the connection to it's owning PhoneWindow.
- * Note: At this time the application can change various attributes of the DecorView which
- * will break things (in subtle/unexpected ways):
- * <ul>
- * <li>setOutlineProvider</li>
- * <li>setSurfaceFormat</li>
- * <li>..</li>
- * </ul>
- *
- * Here describe the behavior of overlaying caption on the content and drawing.
- *
- * First, no matter where the content View gets added, it will always be the first child and the
- * caption will be the second. This way the caption will always be drawn on top of the content when
- * overlaying is enabled.
- *
- * Second, the touch dispatch is customized to handle overlaying. This is what happens when touch
- * is dispatched on the caption area while overlaying it on content:
- * <ul>
- * <li>DecorCaptionView.onInterceptTouchEvent() will try intercepting the touch events if the
- * down action is performed on top close or maximize buttons; the reason for that is we want these
- * buttons to always work.</li>
- * <li>The caption view will try to consume the event to apply the dragging logic.</li>
- * <li>If the touch event is not consumed by the caption, the content View will receive the touch
- * event</li>
- * </ul>
- */
-public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
-        GestureDetector.OnGestureListener {
-    private PhoneWindow mOwner = null;
-    private boolean mShow = false;
-
-    // True if the window is being dragged.
-    private boolean mDragging = false;
-
-    private boolean mOverlayWithAppContent = false;
-
-    private View mCaption;
-    private View mContent;
-    private View mMaximize;
-    private View mClose;
-
-    // Fields for detecting drag events.
-    private int mTouchDownX;
-    private int mTouchDownY;
-    private boolean mCheckForDragging;
-    private int mDragSlop;
-
-    // Fields for detecting and intercepting click events on close/maximize.
-    private ArrayList<View> mTouchDispatchList = new ArrayList<>(2);
-    // We use the gesture detector to detect clicks on close/maximize buttons and to be consistent
-    // with existing click detection.
-    private GestureDetector mGestureDetector;
-    private final Rect mCloseRect = new Rect();
-    private final Rect mMaximizeRect = new Rect();
-    private View mClickTarget;
-    private int mRootScrollY;
-
-    public DecorCaptionView(Context context) {
-        super(context);
-        init(context);
-    }
-
-    public DecorCaptionView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(context);
-    }
-
-    public DecorCaptionView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        init(context);
-    }
-
-    private void init(Context context) {
-        mDragSlop = ViewConfiguration.get(context).getScaledTouchSlop();
-        mGestureDetector = new GestureDetector(context, this);
-        setContentDescription(context.getString(R.string.accessibility_freeform_caption,
-                context.getPackageManager().getApplicationLabel(context.getApplicationInfo())));
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mCaption = getChildAt(0);
-    }
-
-    public void setPhoneWindow(PhoneWindow owner, boolean show) {
-        mOwner = owner;
-        mShow = show;
-        mOverlayWithAppContent = owner.isOverlayWithDecorCaptionEnabled();
-        updateCaptionVisibility();
-        // By changing the outline provider to BOUNDS, the window can remove its
-        // background without removing the shadow.
-        mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
-        mMaximize = findViewById(R.id.maximize_window);
-        mClose = findViewById(R.id.close_window);
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        // If the user starts touch on the maximize/close buttons, we immediately intercept, so
-        // that these buttons are always clickable.
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            final int x = (int) ev.getX();
-            final int y = (int) ev.getY();
-            // Only offset y for containment tests because the actual views are already translated.
-            if (mMaximizeRect.contains(x, y - mRootScrollY)) {
-                mClickTarget = mMaximize;
-            }
-            if (mCloseRect.contains(x, y - mRootScrollY)) {
-                mClickTarget = mClose;
-            }
-        }
-        return mClickTarget != null;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (mClickTarget != null) {
-            mGestureDetector.onTouchEvent(event);
-            final int action = event.getAction();
-            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-                mClickTarget = null;
-            }
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onTouch(View v, MotionEvent e) {
-        // Note: There are no mixed events. When a new device gets used (e.g. 1. Mouse, 2. touch)
-        // the old input device events get cancelled first. So no need to remember the kind of
-        // input device we are listening to.
-        final int x = (int) e.getX();
-        final int y = (int) e.getY();
-        final boolean fromMouse = e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE;
-        final boolean primaryButton = (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0;
-        final int actionMasked = e.getActionMasked();
-        switch (actionMasked) {
-            case MotionEvent.ACTION_DOWN:
-                if (!mShow) {
-                    // When there is no caption we should not react to anything.
-                    return false;
-                }
-                // Checking for a drag action is started if we aren't dragging already and the
-                // starting event is either a left mouse button or any other input device.
-                if (!fromMouse || primaryButton) {
-                    mCheckForDragging = true;
-                    mTouchDownX = x;
-                    mTouchDownY = y;
-                }
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                if (!mDragging && mCheckForDragging && (fromMouse || passedSlop(x, y))) {
-                    mCheckForDragging = false;
-                    mDragging = true;
-                    startMovingTask(e.getRawX(), e.getRawY());
-                    // After the above call the framework will take over the input.
-                    // This handler will receive ACTION_CANCEL soon (possible after a few spurious
-                    // ACTION_MOVE events which are safe to ignore).
-                }
-                break;
-
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                if (!mDragging) {
-                    break;
-                }
-                // Abort the ongoing dragging.
-                if (actionMasked == MotionEvent.ACTION_UP) {
-                    // If it receives ACTION_UP event, the dragging is already finished and also
-                    // the system can not end drag on ACTION_UP event. So request to finish
-                    // dragging.
-                    finishMovingTask();
-                }
-                mDragging = false;
-                return !mCheckForDragging;
-        }
-        return mDragging || mCheckForDragging;
-    }
-
-    @Override
-    public boolean shouldDelayChildPressedState() {
-        return false;
-    }
-
-    private boolean passedSlop(int x, int y) {
-        return Math.abs(x - mTouchDownX) > mDragSlop || Math.abs(y - mTouchDownY) > mDragSlop;
-    }
-
-    /**
-     * The phone window configuration has changed and the caption needs to be updated.
-     * @param show True if the caption should be shown.
-     */
-    public void onConfigurationChanged(boolean show) {
-        mShow = show;
-        updateCaptionVisibility();
-    }
-
-    @Override
-    public void addView(View child, int index, ViewGroup.LayoutParams params) {
-        if (!(params instanceof MarginLayoutParams)) {
-            throw new IllegalArgumentException(
-                    "params " + params + " must subclass MarginLayoutParams");
-        }
-        // Make sure that we never get more then one client area in our view.
-        if (index >= 2 || getChildCount() >= 2) {
-            throw new IllegalStateException("DecorCaptionView can only handle 1 client view");
-        }
-        // To support the overlaying content in the caption, we need to put the content view as the
-        // first child to get the right Z-Ordering.
-        super.addView(child, 0, params);
-        mContent = child;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int captionHeight;
-        if (mCaption.getVisibility() != View.GONE) {
-            measureChildWithMargins(mCaption, widthMeasureSpec, 0, heightMeasureSpec, 0);
-            captionHeight = mCaption.getMeasuredHeight();
-        } else {
-            captionHeight = 0;
-        }
-        if (mContent != null) {
-            if (mOverlayWithAppContent) {
-                measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
-            } else {
-                measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec,
-                        captionHeight);
-            }
-        }
-
-        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
-                MeasureSpec.getSize(heightMeasureSpec));
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        final int captionHeight;
-        if (mCaption.getVisibility() != View.GONE) {
-            mCaption.layout(0, 0, mCaption.getMeasuredWidth(), mCaption.getMeasuredHeight());
-            captionHeight = mCaption.getBottom() - mCaption.getTop();
-            mMaximize.getHitRect(mMaximizeRect);
-            mClose.getHitRect(mCloseRect);
-        } else {
-            captionHeight = 0;
-            mMaximizeRect.setEmpty();
-            mCloseRect.setEmpty();
-        }
-
-        if (mContent != null) {
-            if (mOverlayWithAppContent) {
-                mContent.layout(0, 0, mContent.getMeasuredWidth(), mContent.getMeasuredHeight());
-            } else {
-                mContent.layout(0, captionHeight, mContent.getMeasuredWidth(),
-                        captionHeight + mContent.getMeasuredHeight());
-            }
-        }
-
-        ((DecorView) mOwner.getDecorView()).notifyCaptionHeightChanged();
-
-        // This assumes that the caption bar is at the top.
-        mOwner.notifyRestrictedCaptionAreaCallback(mMaximize.getLeft(), mMaximize.getTop(),
-                mClose.getRight(), mClose.getBottom());
-    }
-
-    /**
-     * Updates the visibility of the caption.
-     **/
-    private void updateCaptionVisibility() {
-        mCaption.setVisibility(mShow ? VISIBLE : GONE);
-        mCaption.setOnTouchListener(this);
-    }
-
-    /**
-     * Maximize or restore the window by moving it to the maximized or freeform workspace stack.
-     **/
-    private void toggleFreeformWindowingMode() {
-        Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
-        if (callback != null) {
-            callback.toggleFreeformWindowingMode();
-        }
-    }
-
-    public boolean isCaptionShowing() {
-        return mShow;
-    }
-
-    public int getCaptionHeight() {
-        return (mCaption != null) ? mCaption.getHeight() : 0;
-    }
-
-    public void removeContentView() {
-        if (mContent != null) {
-            removeView(mContent);
-            mContent = null;
-        }
-    }
-
-    public View getCaption() {
-        return mCaption;
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new MarginLayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected LayoutParams generateDefaultLayoutParams() {
-        return new MarginLayoutParams(MarginLayoutParams.MATCH_PARENT,
-                MarginLayoutParams.MATCH_PARENT);
-    }
-
-    @Override
-    protected LayoutParams generateLayoutParams(LayoutParams p) {
-        return new MarginLayoutParams(p);
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof MarginLayoutParams;
-    }
-
-    @Override
-    public boolean onDown(MotionEvent e) {
-        return false;
-    }
-
-    @Override
-    public void onShowPress(MotionEvent e) {
-
-    }
-
-    @Override
-    public boolean onSingleTapUp(MotionEvent e) {
-        if (mClickTarget == mMaximize) {
-            toggleFreeformWindowingMode();
-        } else if (mClickTarget == mClose) {
-            mOwner.dispatchOnWindowDismissed(
-                    true /*finishTask*/, false /*suppressWindowTransition*/);
-        }
-        return true;
-    }
-
-    @Override
-    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
-        return false;
-    }
-
-    @Override
-    public void onLongPress(MotionEvent e) {
-
-    }
-
-    @Override
-    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-        return false;
-    }
-
-    /**
-     * Called when {@link android.view.ViewRootImpl} scrolls for adjustPan.
-     */
-    public void onRootViewScrollYChanged(int scrollY) {
-        // Offset the caption opposite the root scroll. This keeps the caption at the
-        // top of the window during adjustPan.
-        if (mCaption != null) {
-            mRootScrollY = scrollY;
-            mCaption.setTranslationY(scrollY);
-        }
-    }
-}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 03b57d0..773823d 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -21,6 +21,7 @@
     config_namespace: "ANDROID",
     bool_variables: [
         "release_binder_death_recipient_weak_from_jni",
+        "release_package_libandroid_runtime_punch_holes",
     ],
     properties: [
         "cflags",
@@ -63,6 +64,9 @@
         release_binder_death_recipient_weak_from_jni: {
             cflags: ["-DBINDER_DEATH_RECIPIENT_WEAK_FROM_JNI"],
         },
+        release_package_libandroid_runtime_punch_holes: {
+            cflags: ["-DENABLE_PUNCH_HOLES"],
+        },
     },
 
     cpp_std: "gnu++20",
@@ -123,6 +127,7 @@
             srcs: [
                 "AndroidRuntime.cpp",
                 "com_android_internal_content_F2fsUtils.cpp",
+                "com_android_internal_content_FileSystemUtils.cpp",
                 "com_android_internal_content_NativeLibraryHelper.cpp",
                 "com_google_android_gles_jni_EGLImpl.cpp",
                 "com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp
new file mode 100644
index 0000000..4bd2d72
--- /dev/null
+++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "FileSystemUtils"
+
+#include "com_android_internal_content_FileSystemUtils.h"
+
+#include <android-base/file.h>
+#include <android-base/hex.h>
+#include <android-base/unique_fd.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+#include <array>
+#include <fstream>
+#include <vector>
+
+using android::base::HexString;
+using android::base::ReadFullyAtOffset;
+
+namespace android {
+bool punchHoles(const char *filePath, const uint64_t offset,
+                const std::vector<Elf64_Phdr> &programHeaders) {
+    struct stat64 beforePunch;
+    lstat64(filePath, &beforePunch);
+    uint64_t blockSize = beforePunch.st_blksize;
+    IF_ALOGD() {
+        ALOGD("Total number of LOAD segments %zu", programHeaders.size());
+
+        ALOGD("Size before punching holes st_blocks: %" PRIu64
+              ", st_blksize: %ld, st_size: %" PRIu64 "",
+              beforePunch.st_blocks, beforePunch.st_blksize,
+              static_cast<uint64_t>(beforePunch.st_size));
+    }
+
+    android::base::unique_fd fd(open(filePath, O_RDWR | O_CLOEXEC));
+    if (!fd.ok()) {
+        ALOGE("Can't open file to punch %s", filePath);
+        return false;
+    }
+
+    // read in chunks of 64KB
+    constexpr uint64_t kChunkSize = 64 * 1024;
+
+    // malloc is used to gracefully handle oom which might occur during the allocation of buffer.
+    // allocating using new or vector here results in oom/exception on failure where as malloc will
+    // return nullptr.
+    std::unique_ptr<uint8_t, decltype(&free)> buffer(static_cast<uint8_t *>(malloc(kChunkSize)),
+                                                     &free);
+    if (buffer == nullptr) {
+        ALOGE("Failed to allocate read buffer");
+        return false;
+    }
+
+    for (size_t index = 0; programHeaders.size() >= 2 && index < programHeaders.size() - 1;
+         index++) {
+        // find LOAD segments from program headers, calculate padding and punch holes
+        uint64_t punchOffset;
+        if (__builtin_add_overflow(programHeaders[index].p_offset, programHeaders[index].p_filesz,
+                                   &punchOffset)) {
+            ALOGE("Overflow occurred when adding offset and filesize");
+            return false;
+        }
+
+        uint64_t punchLen;
+        if (__builtin_sub_overflow(programHeaders[index + 1].p_offset, punchOffset, &punchLen)) {
+            ALOGE("Overflow occurred when calculating length");
+            return false;
+        }
+
+        if (punchLen < blockSize) {
+            continue;
+        }
+
+        uint64_t punchStartOffset;
+        if (__builtin_add_overflow(offset, punchOffset, &punchStartOffset)) {
+            ALOGE("Overflow occurred when calculating length");
+            return false;
+        }
+
+        uint64_t position = punchStartOffset;
+        uint64_t endPosition;
+        if (__builtin_add_overflow(position, punchLen, &endPosition)) {
+            ALOGE("Overflow occurred when calculating length");
+            return false;
+        }
+
+        // Read content in kChunkSize and verify it is zero
+        while (position <= endPosition) {
+            uint64_t uncheckedChunkEnd;
+            if (__builtin_add_overflow(position, kChunkSize, &uncheckedChunkEnd)) {
+                ALOGE("Overflow occurred when calculating uncheckedChunkEnd");
+                return false;
+            }
+
+            uint64_t readLength;
+            if (__builtin_sub_overflow(std::min(uncheckedChunkEnd, endPosition), position,
+                                       &readLength)) {
+                ALOGE("Overflow occurred when calculating readLength");
+                return false;
+            }
+
+            if (!ReadFullyAtOffset(fd, buffer.get(), readLength, position)) {
+                ALOGE("Failed to read content to punch holes");
+                return false;
+            }
+
+            IF_ALOGD() {
+                ALOGD("Punching holes for length:%" PRIu64 " content which should be zero: %s",
+                      readLength, HexString(buffer.get(), readLength).c_str());
+            }
+
+            bool isZero = std::all_of(buffer.get(), buffer.get() + readLength,
+                                      [](uint8_t i) constexpr { return i == 0; });
+            if (!isZero) {
+                ALOGE("Found non zero content while trying to punch hole. Skipping operation");
+                return false;
+            }
+
+            position = uncheckedChunkEnd;
+        }
+
+        // if we have a uncompressed file which is being opened from APK, use the offset to
+        // punch native lib inside Apk.
+        int result = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, punchStartOffset,
+                               punchLen);
+        if (result < 0) {
+            ALOGE("fallocate failed to punch hole, error:%d", errno);
+            return false;
+        }
+    }
+
+    IF_ALOGD() {
+        struct stat64 afterPunch;
+        lstat64(filePath, &afterPunch);
+        ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64
+              "",
+              afterPunch.st_blocks, afterPunch.st_blksize,
+              static_cast<uint64_t>(afterPunch.st_size));
+    }
+
+    return true;
+}
+
+bool punchHolesInElf64(const char *filePath, const uint64_t offset) {
+    // Open Elf file
+    Elf64_Ehdr ehdr;
+    std::ifstream inputStream(filePath, std::ifstream::in);
+
+    // If this is a zip file, set the offset so that we can read elf file directly
+    inputStream.seekg(offset);
+    // read executable headers
+    inputStream.read((char *)&ehdr, sizeof(ehdr));
+    if (!inputStream.good()) {
+        return false;
+    }
+
+    // only consider elf64 for punching holes
+    if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) {
+        ALOGE("Provided file is not ELF64");
+        return false;
+    }
+
+    // read the program headers from elf file
+    uint64_t programHeaderOffset = ehdr.e_phoff;
+    uint16_t programHeaderNum = ehdr.e_phnum;
+
+    IF_ALOGD() {
+        ALOGD("Punching holes in file: %s programHeaderOffset: %" PRIu64 " programHeaderNum: %hu",
+              filePath, programHeaderOffset, programHeaderNum);
+    }
+
+    // if this is a zip file, also consider elf offset inside a file
+    uint64_t phOffset;
+    if (__builtin_add_overflow(offset, programHeaderOffset, &phOffset)) {
+        ALOGE("Overflow occurred when calculating phOffset");
+        return false;
+    }
+    inputStream.seekg(phOffset);
+
+    std::vector<Elf64_Phdr> programHeaders;
+    for (int headerIndex = 0; headerIndex < programHeaderNum; headerIndex++) {
+        Elf64_Phdr header;
+        inputStream.read((char *)&header, sizeof(header));
+        if (!inputStream.good()) {
+            return false;
+        }
+
+        if (header.p_type != PT_LOAD) {
+            continue;
+        }
+        programHeaders.push_back(header);
+    }
+
+    return punchHoles(filePath, offset, programHeaders);
+}
+
+}; // namespace android
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.h b/core/jni/com_android_internal_content_FileSystemUtils.h
new file mode 100644
index 0000000..a6b145c
--- /dev/null
+++ b/core/jni/com_android_internal_content_FileSystemUtils.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <sys/types.h>
+
+namespace android {
+
+/*
+ * This function deallocates space used by zero padding at the end of LOAD segments in given
+ * uncompressed ELF file. Read ELF headers and find out the offset and sizes of LOAD segments.
+ * [fallocate(2)](http://man7.org/linux/man-pages/man2/fallocate.2.html) is used to deallocate the
+ * zero ranges at the end of LOAD segments. If ELF file is present inside of ApK/Zip file, offset to
+ * the start of the ELF file should be provided.
+ */
+bool punchHolesInElf64(const char* filePath, uint64_t offset);
+
+} // namespace android
\ No newline at end of file
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 149e57a..faa83f8 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -36,6 +36,7 @@
 
 #include <memory>
 
+#include "com_android_internal_content_FileSystemUtils.h"
 #include "core_jni_helpers.h"
 
 #define RS_BITCODE_SUFFIX ".bc"
@@ -169,6 +170,15 @@
             return INSTALL_FAILED_INVALID_APK;
         }
 
+#ifdef ENABLE_PUNCH_HOLES
+        // if library is uncompressed, punch hole in it in place
+        if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
+            ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
+                  "%" PRIu64 "",
+                  fileName, zipFile->getZipFileName(), offset);
+        }
+#endif // ENABLE_PUNCH_HOLES
+
         return INSTALL_SUCCEEDED;
     }
 
diff --git a/core/tests/FileSystemUtilsTest/Android.bp b/core/tests/FileSystemUtilsTest/Android.bp
new file mode 100644
index 0000000..53c22df
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/Android.bp
@@ -0,0 +1,78 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+    default_team: "trendy_team_android_kernel",
+}
+
+cc_library {
+    name: "libpunchtest",
+    stl: "none",
+    host_supported: true,
+    srcs: ["jni/android_test_jni_source.cpp"],
+    header_libs: ["jni_headers"],
+}
+
+android_test_helper_app {
+    name: "embedded_native_libs_test_app",
+    srcs: ["apk_embedded_native_libs/src/**/*.java"],
+    manifest: "apk_embedded_native_libs/embedded_native_libs_test_app.xml",
+    compile_multilib: "64",
+    jni_libs: [
+        "libpunchtest",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "platform-test-annotations",
+    ],
+    use_embedded_native_libs: true,
+}
+
+android_test_helper_app {
+    name: "extract_native_libs_test_app",
+    srcs: ["apk_extract_native_libs/src/**/*.java"],
+    manifest: "apk_extract_native_libs/extract_native_libs_test_app.xml",
+    compile_multilib: "64",
+    jni_libs: [
+        "libpunchtest",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "platform-test-annotations",
+    ],
+    use_embedded_native_libs: false,
+}
+
+java_test_host {
+    name: "FileSystemUtilsTests",
+    // Include all test java files
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "junit",
+        "platform-test-annotations",
+        "truth",
+    ],
+    libs: [
+        "tradefed",
+        "compatibility-host-util",
+        "compatibility-tradefed",
+    ],
+    data: [
+        ":embedded_native_libs_test_app",
+        ":extract_native_libs_test_app",
+    ],
+    test_suites: ["general-tests"],
+    test_config: "AndroidTest.xml",
+}
diff --git a/core/tests/FileSystemUtilsTest/AndroidManifest.xml b/core/tests/FileSystemUtilsTest/AndroidManifest.xml
new file mode 100644
index 0000000..acd5ef3
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          android:installLocation="internalOnly"
+          package="com.android.internal.content.fstests">
+
+    <instrumentation
+            android:name="androidx.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.internal.content.fstests"
+            android:label="Frameworks FileSystemUtils Tests" />
+
+</manifest>
diff --git a/core/tests/FileSystemUtilsTest/AndroidTest.xml b/core/tests/FileSystemUtilsTest/AndroidTest.xml
new file mode 100644
index 0000000..27f49b2
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<configuration description="Runs FileSystemUtilsTest.">
+    <option name="test-suite-tag" value="apct"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="embedded_native_libs_test_app.apk" />
+        <option name="test-file-name" value="extract_native_libs_test_app.apk" />
+    </target_preparer>
+
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="FileSystemUtilsTests.jar" />
+    </test>
+</configuration>
diff --git a/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/embedded_native_libs_test_app.xml b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/embedded_native_libs_test_app.xml
new file mode 100644
index 0000000..868f7f3
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/embedded_native_libs_test_app.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.test.embedded">
+    <application android:extractNativeLibs="false">
+        <uses-library android:name="android.test.runner"/>
+        <activity android:name=".MainActivity"
+                  android:exported="true"
+                  android:process=":NewProcess">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+    </application>
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.test.embedded"/>
+</manifest>
\ No newline at end of file
diff --git a/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/MainActivity.java b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/MainActivity.java
new file mode 100644
index 0000000..efa2a39
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/MainActivity.java
@@ -0,0 +1,60 @@
+/*
+ * 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 android.test.embedded;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
+
+public class MainActivity extends Activity {
+
+    static {
+        System.loadLibrary("punchtest");
+    }
+
+    @VisibleForTesting
+    static final String INTENT_TYPE = "android.test.embedded.EMBEDDED_LIB_LOADED";
+
+    @VisibleForTesting
+    static final String KEY_OPERAND_1 = "OP1";
+
+    @VisibleForTesting
+    static final String KEY_OPERAND_2 = "OP2";
+
+    @VisibleForTesting
+    static final String KEY_RESULT = "RESULT";
+
+    @Override
+    public void onCreate(Bundle savedOnstanceState) {
+        super.onCreate(savedOnstanceState);
+
+        Intent received =  getIntent();
+        int op1 = received.getIntExtra(KEY_OPERAND_1, -1);
+        int op2 = received.getIntExtra(KEY_OPERAND_2, -1);
+        int result = add(op1, op2);
+
+        // Send broadcast so that test can know app has launched and lib is loaded
+        // attach result which has been fetched from JNI lib
+        Intent intent = new Intent(INTENT_TYPE);
+        intent.putExtra(KEY_RESULT, result);
+        sendBroadcast(intent);
+    }
+
+    private native int add(int op1, int op2);
+}
diff --git a/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/PunchEmbeddedLibTest.java b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/PunchEmbeddedLibTest.java
new file mode 100644
index 0000000..d7d67b8
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/PunchEmbeddedLibTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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 android.test.embedded;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PunchEmbeddedLibTest {
+
+    @Test
+    public void testPunchedNativeLibs_embeddedLib() throws Exception {
+        Context context = InstrumentationRegistry.getContext();
+        CountDownLatch receivedSignal = new CountDownLatch(1);
+
+        // Test app is expected to receive this and perform addition of operands using punched lib
+        int op1 = 48;
+        int op2 = 75;
+        IntentFilter intentFilter = new IntentFilter(MainActivity.INTENT_TYPE);
+        BroadcastReceiver broadcastReceiver =
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        receivedSignal.countDown();
+                        int result = intent.getIntExtra(MainActivity.KEY_RESULT, 1000);
+                        Assert.assertEquals(result, op1 + op2);
+
+                    }
+                };
+        context.registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED);
+
+        Intent launchIntent =
+                context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
+        launchIntent.putExtra(MainActivity.KEY_OPERAND_1, op1);
+        launchIntent.putExtra(MainActivity.KEY_OPERAND_2, op2);
+        context.startActivity(launchIntent);
+
+        Assert.assertTrue("Failed to launch app", receivedSignal.await(10, TimeUnit.SECONDS));
+    }
+}
diff --git a/core/tests/FileSystemUtilsTest/apk_extract_native_libs/extract_native_libs_test_app.xml b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/extract_native_libs_test_app.xml
new file mode 100644
index 0000000..6db96f7
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/extract_native_libs_test_app.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.test.extract">
+    <application android:extractNativeLibs="true">
+        <uses-library android:name="android.test.runner"/>
+        <activity android:name=".MainActivity"
+                  android:exported="true"
+                  android:process=":NewProcess">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+    </application>
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.test.extract"/>
+</manifest>
\ No newline at end of file
diff --git a/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/MainActivity.java b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/MainActivity.java
new file mode 100644
index 0000000..b1c157e
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/MainActivity.java
@@ -0,0 +1,60 @@
+/*
+ * 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 android.test.extract;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
+
+public class MainActivity extends Activity {
+
+    static {
+        System.loadLibrary("punchtest");
+    }
+
+    @VisibleForTesting
+    static final String INTENT_TYPE = "android.test.extract.EXTRACTED_LIB_LOADED";
+
+    @VisibleForTesting
+    static final String KEY_OPERAND_1 = "OP1";
+
+    @VisibleForTesting
+    static final String KEY_OPERAND_2 = "OP2";
+
+    @VisibleForTesting
+    static final String KEY_RESULT = "RESULT";
+
+    @Override
+    public void onCreate(Bundle savedOnstanceState) {
+        super.onCreate(savedOnstanceState);
+
+        Intent received =  getIntent();
+        int op1 = received.getIntExtra(KEY_OPERAND_1, -1);
+        int op2 = received.getIntExtra(KEY_OPERAND_2, -1);
+        int result = subtract(op1, op2);
+
+        // Send broadcast so that test can know app has launched and lib is loaded
+        // attach result which has been fetched from JNI lib
+        Intent intent = new Intent(INTENT_TYPE);
+        intent.putExtra(KEY_RESULT, result);
+        sendBroadcast(intent);
+    }
+
+    private native int subtract(int op1, int op2);
+}
diff --git a/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/PunchExtractedLibTest.java b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/PunchExtractedLibTest.java
new file mode 100644
index 0000000..7cc1017
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/PunchExtractedLibTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.test.extract;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PunchExtractedLibTest {
+
+    @Test
+    public void testPunchedNativeLibs_extractedLib() throws Exception {
+        Context context = InstrumentationRegistry.getContext();
+        CountDownLatch receivedSignal = new CountDownLatch(1);
+
+        // Test app is expected to receive this and perform subtraction using extracted lib
+        int op1 = 100;
+        int op2 = 71;
+        IntentFilter intentFilter = new IntentFilter(MainActivity.INTENT_TYPE);
+        BroadcastReceiver broadcastReceiver =
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        receivedSignal.countDown();
+                        int result = intent.getIntExtra(MainActivity.KEY_RESULT, 1000);
+                        Assert.assertEquals(result, op1 - op2);
+                    }
+                };
+        context.registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED);
+
+        Intent launchIntent =
+                context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
+        launchIntent.putExtra(MainActivity.KEY_OPERAND_1, op1);
+        launchIntent.putExtra(MainActivity.KEY_OPERAND_2, op2);
+        context.startActivity(launchIntent);
+
+        Assert.assertTrue("Failed to launch app", receivedSignal.await(10, TimeUnit.SECONDS));
+    }
+}
diff --git a/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp b/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp
new file mode 100644
index 0000000..2a5ba81
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#include <jni.h>
+
+// This will be called from embedded_native_libs_test_app
+extern "C" JNIEXPORT
+jint JNICALL Java_android_test_embedded_MainActivity_add(JNIEnv*, jclass, jint op1, jint op2) {
+    return op1 + op2;
+}
+
+// This will be called from extract_native_libs_test_app
+extern "C" JNIEXPORT
+jint JNICALL Java_android_test_extract_MainActivity_subtract(JNIEnv*, jclass, jint op1, jint op2) {
+    return op1 - op2;
+}
+
+// Initialize JNI
+jint JNI_OnLoad(JavaVM *jvm, void */* reserved */) {
+    JNIEnv *e;
+
+    // Check JNI version
+    if (jvm->GetEnv((void **) &e, JNI_VERSION_1_6)) {
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_6;
+}
diff --git a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
new file mode 100644
index 0000000..77802e5
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.internal.content;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class FileSystemUtilsTest extends BaseHostJUnit4Test {
+
+    @Test
+    @AppModeFull
+    public void runPunchedApp_embeddedNativeLibs() throws DeviceNotAvailableException {
+        String appPackage = "android.test.embedded";
+        String testName = "PunchEmbeddedLibTest";
+        assertTrue(isPackageInstalled(appPackage));
+        runDeviceTests(appPackage, appPackage + "." + testName);
+    }
+
+    @Test
+    @AppModeFull
+    public void runPunchedApp_extractedNativeLibs() throws DeviceNotAvailableException {
+        String appPackage = "android.test.extract";
+        String testName = "PunchExtractedLibTest";
+        assertTrue(isPackageInstalled(appPackage));
+        runDeviceTests(appPackage, appPackage + "." + testName);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 97f894f..4fb85c1f 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -25,9 +25,7 @@
 import static android.view.InsetsSource.ID_IME;
 import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED;
 import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY;
-import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
 import static android.view.WindowInsets.Type.all;
-import static android.view.WindowInsets.Type.captionBar;
 import static android.view.WindowInsets.Type.defaultVisible;
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.navigationBars;
@@ -49,7 +47,6 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -805,48 +802,6 @@
     }
 
     @Test
-    public void testCaptionInsetsStateAssemble() {
-        if (CAPTION_ON_SHELL) {
-            // For this case, the test is covered by WindowContainerInsetsSourceProviderTest, This
-            // test can be removed after the caption is moved to shell completely.
-            return;
-        }
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            final Rect frame = new Rect(0, 0, 100, 300);
-            final int captionBarHeight = 100;
-            final InsetsState state = mController.getState();
-            mController.onFrameChanged(frame);
-            mController.setCaptionInsetsHeight(captionBarHeight);
-            // The caption bar insets height should be the same as the caption bar height.
-            assertEquals(captionBarHeight, state.calculateInsets(frame, captionBar(), false).top);
-            // Test update to remove the caption bar
-            mController.setCaptionInsetsHeight(0);
-            // The caption bar source should not be there at all, because we don't add empty
-            // caption to the state from the server.
-            for (int i = state.sourceSize() - 1; i >= 0; i--) {
-                assertNotEquals(captionBar(), state.sourceAt(i).getType());
-            }
-        });
-    }
-
-    @Test
-    public void testNotifyCaptionInsetsOnlyChange() {
-        if (CAPTION_ON_SHELL) {
-            // For this case, the test is covered by WindowContainerInsetsSourceProviderTest, This
-            // test can be removed after the caption is moved to shell completely.
-            return;
-        }
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            reset(mTestHost);
-            mController.setCaptionInsetsHeight(100);
-            verify(mTestHost).notifyInsetsChanged();
-            reset(mTestHost);
-            mController.setCaptionInsetsHeight(0);
-            verify(mTestHost).notifyInsetsChanged();
-        });
-    }
-
-    @Test
     public void testRequestedState() {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             mController.hide(statusBars() | navigationBars());
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 32c2d1e..51b0a24 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
@@ -40,7 +40,6 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
-import android.view.ViewRootImpl;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowlessWindowManager;
@@ -293,60 +292,56 @@
                 .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
                 .show(mCaptionContainerSurface);
 
-        if (ViewRootImpl.CAPTION_ON_SHELL) {
-            outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
+        outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
 
-            // Caption insets
-            if (mIsCaptionVisible) {
-                // Caption inset is the full width of the task with the |captionHeight| and
-                // positioned at the top of the task bounds, also in absolute coordinates.
-                // So just reuse the task bounds and adjust the bottom coordinate.
-                mCaptionInsetsRect.set(taskBounds);
-                mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + outResult.mCaptionHeight;
+        // Caption insets
+        if (mIsCaptionVisible) {
+            // Caption inset is the full width of the task with the |captionHeight| and
+            // positioned at the top of the task bounds, also in absolute coordinates.
+            // So just reuse the task bounds and adjust the bottom coordinate.
+            mCaptionInsetsRect.set(taskBounds);
+            mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + outResult.mCaptionHeight;
 
-                // Caption bounding rectangles: these are optional, and are used to present finer
-                // insets than traditional |Insets| to apps about where their content is occluded.
-                // These are also in absolute coordinates.
-                final Rect[] boundingRects;
-                final int numOfElements = params.mOccludingCaptionElements.size();
-                if (numOfElements == 0) {
-                    boundingRects = null;
-                } else {
-                    // The customizable region can at most be equal to the caption bar.
+            // Caption bounding rectangles: these are optional, and are used to present finer
+            // insets than traditional |Insets| to apps about where their content is occluded.
+            // These are also in absolute coordinates.
+            final Rect[] boundingRects;
+            final int numOfElements = params.mOccludingCaptionElements.size();
+            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 =
+                            params.mOccludingCaptionElements.get(i);
+                    final int elementWidthPx =
+                            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.set(mCaptionInsetsRect);
-                    }
-                    boundingRects = new Rect[numOfElements];
-                    for (int i = 0; i < numOfElements; i++) {
-                        final OccludingCaptionElement element =
-                                params.mOccludingCaptionElements.get(i);
-                        final int elementWidthPx =
-                                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);
-                        }
+                        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,
-                        boundingRects);
-                wct.addInsetsSource(mTaskInfo.token,
-                        mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
-                        mCaptionInsetsRect, null /* boundingRects */);
-            } else {
-                wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
-                        WindowInsets.Type.captionBar());
-                wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
-                        WindowInsets.Type.mandatorySystemGestures());
             }
+            // Add this caption as an inset source.
+            wct.addInsetsSource(mTaskInfo.token,
+                    mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect,
+                    boundingRects);
+            wct.addInsetsSource(mTaskInfo.token,
+                    mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
+                    mCaptionInsetsRect, null /* boundingRects */);
         } else {
-            startT.hide(mCaptionContainerSurface);
+            wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
+                    WindowInsets.Type.captionBar());
+            wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
+                    WindowInsets.Type.mandatorySystemGestures());
         }
 
         // Task surface itself
@@ -594,8 +589,7 @@
      */
     public void addCaptionInset(WindowContainerTransaction wct) {
         final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode());
-        if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL
-                || !mIsCaptionVisible) {
+        if (captionHeightId == Resources.ID_NULL || !mIsCaptionVisible) {
             return;
         }
 
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 228b25c..5464937 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
@@ -61,7 +61,6 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
-import android.view.ViewRootImpl;
 import android.view.WindowInsets;
 import android.view.WindowManager.LayoutParams;
 import android.window.SurfaceSyncGroup;
@@ -252,16 +251,14 @@
                         argThat(lp -> lp.height == 64
                                 && lp.width == 300
                                 && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
-        if (ViewRootImpl.CAPTION_ON_SHELL) {
-            verify(mMockView).setTaskFocusState(true);
-            verify(mMockWindowContainerTransaction).addInsetsSource(
-                    eq(taskInfo.token),
-                    any(),
-                    eq(0 /* index */),
-                    eq(WindowInsets.Type.captionBar()),
-                    eq(new Rect(100, 300, 400, 364)),
-                    any());
-        }
+        verify(mMockView).setTaskFocusState(true);
+        verify(mMockWindowContainerTransaction).addInsetsSource(
+                eq(taskInfo.token),
+                any(),
+                eq(0 /* index */),
+                eq(WindowInsets.Type.captionBar()),
+                eq(new Rect(100, 300, 400, 364)),
+                any());
 
         verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
         verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index 9d4b426..34a6bc2 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -310,3 +310,7 @@
 
     return true;
 }
+
+const char* ZipFileRO::getZipFileName() {
+    return mFileName;
+}
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index be1f98f..031d2e8 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -187,6 +187,8 @@
      */
     bool uncompressEntry(ZipEntryRO entry, int fd) const;
 
+    const char* getZipFileName();
+
     ~ZipFileRO();
 
 private:
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index a8f3029..e418641 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -296,7 +296,7 @@
 
     private fun listenForSchedulingWatchdog() {
         keyguardTransitionInteractor
-            .transition(from = null, to = KeyguardState.GONE)
+            .transition(to = KeyguardState.GONE)
             .filter { it.transitionState == TransitionState.FINISHED }
             .onEach {
                 // We deliberately want to run this in background because scheduleWatchdog does
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index 04edd25..2d9c14e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
@@ -98,7 +98,7 @@
             .distinctUntilChanged()
 
     val transitionEnded =
-        keyguardTransitionInteractor.transition(from = DREAMING, to = null).filter { step ->
+        keyguardTransitionInteractor.transition(from = DREAMING).filter { step ->
             step.transitionState == TransitionState.FINISHED ||
                 step.transitionState == TransitionState.CANCELED
         }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 612ae6c..db6b8fe 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -337,11 +337,6 @@
             namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
         )
 
-    @Keep
-    @JvmField
-    val WM_CAPTION_ON_SHELL =
-        sysPropBooleanFlag("persist.wm.debug.caption_on_shell", default = true)
-
     // TODO(b/256873975): Tracking Bug
     @JvmField
     @Keep
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index c835599..a65a882 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -84,7 +84,7 @@
 
         applicationScope.launch(bgDispatcher) {
             // We drop 1 to avoid triggering on initial collect().
-            keyguardTransitionInteractor.transition(from = null, to = GONE).collect { transition ->
+            keyguardTransitionInteractor.transition(to = GONE).collect { transition ->
                 if (transition.transitionState == TransitionState.FINISHED) {
                     onKeyguardGone()
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index d3ad0c2a..a18579d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -157,7 +157,7 @@
      * Receive all [TransitionStep] matching a filter of [from]->[to]. Allow nulls in order to match
      * any transition, for instance (any)->GONE.
      */
-    fun transition(from: KeyguardState?, to: KeyguardState?): Flow<TransitionStep> {
+    fun transition(from: KeyguardState? = null, to: KeyguardState? = null): Flow<TransitionStep> {
         if (from == null && to == null) {
             throw IllegalArgumentException("from and to cannot both be null")
         }
@@ -198,8 +198,7 @@
 
     /** The last [TransitionStep] with a [TransitionState] of FINISHED */
     val finishedKeyguardTransitionStep: Flow<TransitionStep> =
-        repository.transitions
-            .filter { step -> step.transitionState == TransitionState.FINISHED }
+        repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
 
     /** The destination state of the last [TransitionState.STARTED] transition. */
     val startedKeyguardState: SharedFlow<KeyguardState> =
@@ -377,7 +376,7 @@
         state: KeyguardState,
     ): Flow<Boolean> {
         return getOrCreateFlow(Edge(from = null, to = state))
-            .mapLatest { it.transitionState.isActive() }
+            .mapLatest { it.transitionState.isTransitioning() }
             .onStart { emit(false) }
             .distinctUntilChanged()
     }
@@ -391,7 +390,7 @@
         to: KeyguardState,
     ): Flow<Boolean> {
         return getOrCreateFlow(Edge(from = from, to = to))
-            .mapLatest { it.transitionState.isActive() }
+            .mapLatest { it.transitionState.isTransitioning() }
             .onStart { emit(false) }
             .distinctUntilChanged()
     }
@@ -403,7 +402,7 @@
         state: KeyguardState,
     ): Flow<Boolean> {
         return getOrCreateFlow(Edge(from = state, to = null))
-            .mapLatest { it.transitionState.isActive() }
+            .mapLatest { it.transitionState.isTransitioning() }
             .onStart { emit(false) }
             .distinctUntilChanged()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
index f6567a6..1cd188c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
@@ -19,20 +19,20 @@
 enum class TransitionState {
     /* Transition has begun. */
     STARTED {
-        override fun isActive() = true
+        override fun isTransitioning() = true
     },
     /* Transition is actively running. */
     RUNNING {
-        override fun isActive() = true
+        override fun isTransitioning() = true
     },
     /* Transition has completed successfully. */
     FINISHED {
-        override fun isActive() = false
+        override fun isTransitioning() = false
     },
     /* Transition has been interrupted, and not completed successfully. */
     CANCELED {
-        override fun isActive() = false
+        override fun isTransitioning() = false
     };
 
-    abstract fun isActive(): Boolean
+    abstract fun isTransitioning(): Boolean
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index e2177e6..d4844e2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -94,9 +94,9 @@
                     occludedToLockscreen,
                     aodToLockscreen ->
                     val translationY =
-                        if (aodToLockscreen.transitionState.isActive()) {
+                        if (aodToLockscreen.transitionState.isTransitioning()) {
                             aodToLockscreen.value ?: 0f
-                        } else if (goneToAod.transitionState.isActive()) {
+                        } else if (goneToAod.transitionState.isTransitioning()) {
                             (goneToAod.value ?: 0f) + burnInModel.translationY
                         } else {
                             burnInModel.translationY + occludedToLockscreen + keyguardTranslationY
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 5b39ed3..d15d45a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -603,7 +603,7 @@
     internal fun listenForAnyStateToGoneKeyguardTransition(scope: CoroutineScope): Job {
         return scope.launch {
             keyguardTransitionInteractor
-                .transition(from = null, to = GONE)
+                .transition(to = GONE)
                 .filter { it.transitionState == TransitionState.FINISHED }
                 .collect {
                     showMediaCarousel()
@@ -616,7 +616,7 @@
     internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
         return scope.launch {
             keyguardTransitionInteractor
-                .transition(from = null, to = LOCKSCREEN)
+                .transition(to = LOCKSCREEN)
                 .filter { it.transitionState == TransitionState.FINISHED }
                 .collect {
                     if (!allowMediaPlayerOnLockScreen) {