Merge "VCN: Refactor getPacketLossRatePercentage to return an object" into main
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 65ec4d4..3e277e8 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -54,34 +54,34 @@
             baseline_file: ":non-updatable-lint-baseline.txt",
         },
     },
-    dists: [
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/public/api",
-            dest: "android-non-updatable.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/public/api",
-            dest: "android-non-updatable-removed.txt",
-        },
-    ],
     soong_config_variables: {
         release_hidden_api_exportable_stubs: {
             dists: [
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/public/api",
+                    dest: "android-non-updatable.txt",
                     tag: ".exportable.api.txt",
                 },
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/public/api",
+                    dest: "android-non-updatable-removed.txt",
                     tag: ".exportable.removed-api.txt",
                 },
             ],
             conditions_default: {
                 dists: [
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/public/api",
+                        dest: "android-non-updatable.txt",
                         tag: ".api.txt",
                     },
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/public/api",
+                        dest: "android-non-updatable-removed.txt",
                         tag: ".removed-api.txt",
                     },
                 ],
@@ -134,34 +134,34 @@
             baseline_file: ":non-updatable-system-lint-baseline.txt",
         },
     },
-    dists: [
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/system/api",
-            dest: "android-non-updatable.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/system/api",
-            dest: "android-non-updatable-removed.txt",
-        },
-    ],
     soong_config_variables: {
         release_hidden_api_exportable_stubs: {
             dists: [
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/system/api",
+                    dest: "android-non-updatable.txt",
                     tag: ".exportable.api.txt",
                 },
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/system/api",
+                    dest: "android-non-updatable-removed.txt",
                     tag: ".exportable.removed-api.txt",
                 },
             ],
             conditions_default: {
                 dists: [
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/system/api",
+                        dest: "android-non-updatable.txt",
                         tag: ".api.txt",
                     },
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/system/api",
+                        dest: "android-non-updatable-removed.txt",
                         tag: ".removed-api.txt",
                     },
                 ],
@@ -189,56 +189,58 @@
             baseline_file: ":non-updatable-test-lint-baseline.txt",
         },
     },
-    dists: [
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/test/api",
-            dest: "android.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/test/api",
-            dest: "removed.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/test/api",
-            dest: "android-non-updatable.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/test/api",
-            dest: "android-non-updatable-removed.txt",
-        },
-    ],
     soong_config_variables: {
         release_hidden_api_exportable_stubs: {
             dists: [
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/test/api",
+                    dest: "android.txt",
                     tag: ".exportable.api.txt",
                 },
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/test/api",
+                    dest: "removed.txt",
                     tag: ".exportable.removed-api.txt",
                 },
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/test/api",
+                    dest: "android-non-updatable.txt",
                     tag: ".exportable.api.txt",
                 },
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/test/api",
+                    dest: "android-non-updatable-removed.txt",
                     tag: ".exportable.removed-api.txt",
                 },
             ],
             conditions_default: {
                 dists: [
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/test/api",
+                        dest: "android.txt",
                         tag: ".api.txt",
                     },
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/test/api",
+                        dest: "removed.txt",
                         tag: ".removed-api.txt",
                     },
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/test/api",
+                        dest: "android-non-updatable.txt",
                         tag: ".api.txt",
                     },
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/test/api",
+                        dest: "android-non-updatable-removed.txt",
                         tag: ".removed-api.txt",
                     },
                 ],
@@ -271,34 +273,34 @@
             baseline_file: ":non-updatable-module-lib-lint-baseline.txt",
         },
     },
-    dists: [
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/module-lib/api",
-            dest: "android-non-updatable.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/module-lib/api",
-            dest: "android-non-updatable-removed.txt",
-        },
-    ],
     soong_config_variables: {
         release_hidden_api_exportable_stubs: {
             dists: [
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/module-lib/api",
+                    dest: "android-non-updatable.txt",
                     tag: ".exportable.api.txt",
                 },
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/module-lib/api",
+                    dest: "android-non-updatable-removed.txt",
                     tag: ".exportable.removed-api.txt",
                 },
             ],
             conditions_default: {
                 dists: [
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/module-lib/api",
+                        dest: "android-non-updatable.txt",
                         tag: ".api.txt",
                     },
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/module-lib/api",
+                        dest: "android-non-updatable-removed.txt",
                         tag: ".removed-api.txt",
                     },
                 ],
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/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index a51a740..d9a18d7 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -42,12 +42,6 @@
 
     private static DdmHandleHello mInstance = new DdmHandleHello();
 
-    private static final String[] FRAMEWORK_FEATURES = new String[] {
-        "opengl-tracing",
-        "view-hierarchy",
-        "support_boot_stages"
-    };
-
     /* singleton, do not instantiate */
     private DdmHandleHello() {}
 
@@ -193,22 +187,25 @@
         if (false)
             Log.v("ddm-heap", "Got feature list request");
 
-        int size = 4 + 4 * (vmFeatures.length + FRAMEWORK_FEATURES.length);
-        for (int i = vmFeatures.length-1; i >= 0; i--)
+        String[] fmFeatures = Debug.getFeatureList();
+        int size = 4 + 4 * (vmFeatures.length + fmFeatures.length);
+        for (int i = vmFeatures.length - 1; i >= 0; i--) {
             size += vmFeatures[i].length() * 2;
-        for (int i = FRAMEWORK_FEATURES.length-1; i>= 0; i--)
-            size += FRAMEWORK_FEATURES[i].length() * 2;
+        }
+        for (int i = fmFeatures.length - 1; i >= 0; i--) {
+            size += fmFeatures[i].length() * 2;
+        }
 
         ByteBuffer out = ByteBuffer.allocate(size);
         out.order(ChunkHandler.CHUNK_ORDER);
-        out.putInt(vmFeatures.length + FRAMEWORK_FEATURES.length);
+        out.putInt(vmFeatures.length + fmFeatures.length);
         for (int i = vmFeatures.length-1; i >= 0; i--) {
             out.putInt(vmFeatures[i].length());
             putString(out, vmFeatures[i]);
         }
-        for (int i = FRAMEWORK_FEATURES.length-1; i >= 0; i--) {
-            out.putInt(FRAMEWORK_FEATURES[i].length());
-            putString(out, FRAMEWORK_FEATURES[i]);
+        for (int i = fmFeatures.length - 1; i >= 0; i--) {
+            out.putInt(fmFeatures[i].length());
+            putString(out, fmFeatures[i]);
         }
 
         return new Chunk(CHUNK_FEAT, out);
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index f785cca..a55398a 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -110,6 +110,12 @@
     private static final String DEFAULT_TRACE_BODY = "dmtrace";
     private static final String DEFAULT_TRACE_EXTENSION = ".trace";
 
+    private static final String[] FRAMEWORK_FEATURES = new String[] {
+        "opengl-tracing",
+        "view-hierarchy",
+        "support_boot_stages",
+    };
+
     /**
      * This class is used to retrieved various statistics about the memory mappings for this
      * process. The returned info is broken down by dalvik, native, and other. All results are in kB.
@@ -1106,6 +1112,17 @@
     }
 
     /**
+     * Returns an array of strings that identify Framework features. This is
+     * used by DDMS to determine what sorts of operations the Framework can
+     * perform.
+     *
+     * @hide
+     */
+    public static String[] getFeatureList() {
+        return FRAMEWORK_FEATURES;
+    }
+
+    /**
      * Change the JDWP port.
      *
      * @deprecated no longer needed or useful
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 8c6bf79..6412ddb 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",
@@ -120,6 +124,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/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 2f2215f..d1d7c14 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -16,8 +16,6 @@
 
 package android.security;
 
-import android.compat.annotation.UnsupportedAppUsage;
-
 /**
  * This class provides some constants and helper methods related to Android's Keystore service.
  * This class was originally much larger, but its functionality was superseded by other classes.
@@ -30,11 +28,4 @@
 
     // Used for UID field to indicate the calling UID.
     public static final int UID_SELF = -1;
-
-    private static final KeyStore KEY_STORE = new KeyStore();
-
-    @UnsupportedAppUsage
-    public static KeyStore getInstance() {
-        return KEY_STORE;
-    }
 }
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/proto/src/am_capabilities.proto b/proto/src/am_capabilities.proto
index d97bf81..fc9f7a45 100644
--- a/proto/src/am_capabilities.proto
+++ b/proto/src/am_capabilities.proto
@@ -7,6 +7,16 @@
   string name = 1;
 }
 
+message VMCapability {
+  string name  = 1;
+}
+
+message FrameworkCapability {
+  string name  = 1;
+}
+
 message Capabilities {
   repeated Capability values = 1;
+  repeated VMCapability vm_capabilities = 2;
+  repeated FrameworkCapability framework_capabilities = 3;
 }
diff --git a/services/Android.bp b/services/Android.bp
index 32a8bbb..888e044 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -285,34 +285,34 @@
             baseline_file: "api/lint-baseline.txt",
         },
     },
-    dists: [
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/system-server/api",
-            dest: "android-non-updatable.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/system-server/api",
-            dest: "android-non-updatable-removed.txt",
-        },
-    ],
     soong_config_variables: {
         release_hidden_api_exportable_stubs: {
             dists: [
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/system-server/api",
+                    dest: "android-non-updatable.txt",
                     tag: ".exportable.api.txt",
                 },
                 {
+                    targets: ["sdk"],
+                    dir: "apistubs/android/system-server/api",
+                    dest: "android-non-updatable-removed.txt",
                     tag: ".exportable.removed-api.txt",
                 },
             ],
             conditions_default: {
                 dists: [
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/system-server/api",
+                        dest: "android-non-updatable.txt",
                         tag: ".api.txt",
                     },
                     {
+                        targets: ["sdk"],
+                        dir: "apistubs/android/system-server/api",
+                        dest: "android-non-updatable-removed.txt",
                         tag: ".removed-api.txt",
                     },
                 ],
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 7c66731..de039fb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -96,6 +96,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.IProgressListener;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallback;
@@ -124,6 +125,8 @@
 import com.android.server.am.LowMemDetector.MemFactor;
 import com.android.server.am.nano.Capabilities;
 import com.android.server.am.nano.Capability;
+import com.android.server.am.nano.FrameworkCapability;
+import com.android.server.am.nano.VMCapability;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.utils.Slogf;
@@ -442,6 +445,22 @@
                 capabilities.values[i] = cap;
             }
 
+            String[] vmCapabilities = Debug.getVmFeatureList();
+            capabilities.vmCapabilities = new VMCapability[vmCapabilities.length];
+            for (int i = 0; i < vmCapabilities.length; i++) {
+                VMCapability cap = new VMCapability();
+                cap.name = vmCapabilities[i];
+                capabilities.vmCapabilities[i] = cap;
+            }
+
+            String[] fmCapabilities = Debug.getFeatureList();
+            capabilities.frameworkCapabilities = new FrameworkCapability[fmCapabilities.length];
+            for (int i = 0; i < fmCapabilities.length; i++) {
+                FrameworkCapability cap = new FrameworkCapability();
+                cap.name = fmCapabilities[i];
+                capabilities.frameworkCapabilities[i] = cap;
+            }
+
             try {
                 getRawOutputStream().write(Capabilities.toByteArray(capabilities));
             } catch (IOException e) {
@@ -451,10 +470,16 @@
         } else {
             // Unfortunately we don't have protobuf text format capabilities here.
             // Fallback to line separated list instead for text parser.
-            pw.println("Format: 1");
+            pw.println("Format: 2");
             for (String capability : CAPABILITIES) {
                 pw.println(capability);
             }
+            for (String capability : Debug.getVmFeatureList()) {
+                pw.println("vm:" + capability);
+            }
+            for (String capability : Debug.getFeatureList()) {
+                pw.println("framework:" + capability);
+            }
         }
         return 0;
     }