Merge "Add new 'update_engine_log' GID" into main
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 858956a..34f2c45 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -335,7 +335,7 @@
   ConsumeFd(std::move(output_fd), &result);
   ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x0+dead)");
 
-  if (mte_supported()) {
+  if (mte_supported() && mte_enabled()) {
     // Test that the default TAGGED_ADDR_CTRL value is set.
     ASSERT_MATCH(result, R"(tagged_addr_ctrl: 000000000007fff3)"
                          R"( \(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, mask 0xfffe\))");
@@ -443,7 +443,7 @@
 
 TEST_P(SizeParamCrasherTest, mte_uaf) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -490,7 +490,7 @@
 
 TEST_P(SizeParamCrasherTest, mte_oob_uaf) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -522,7 +522,7 @@
 
 TEST_P(SizeParamCrasherTest, mte_overflow) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -565,7 +565,7 @@
 
 TEST_P(SizeParamCrasherTest, mte_underflow) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -614,7 +614,7 @@
   //     unsubtle chaos is sure to result.
   // https://man7.org/linux/man-pages/man3/longjmp.3.html
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -648,7 +648,7 @@
 
 TEST_F(CrasherTest, mte_async) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -678,7 +678,7 @@
 
 TEST_F(CrasherTest, mte_multiple_causes) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -764,7 +764,7 @@
 
 TEST_F(CrasherTest, mte_register_tag_dump) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -797,7 +797,7 @@
 
 TEST_F(CrasherTest, mte_fault_tag_dump_front_truncated) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -828,7 +828,7 @@
 
 TEST_F(CrasherTest, mte_fault_tag_dump) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -862,7 +862,7 @@
 
 TEST_F(CrasherTest, mte_fault_tag_dump_rear_truncated) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
diff --git a/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
index aad209a..988ca0c 100644
--- a/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
@@ -175,3 +175,8 @@
   ProtoToString();
   EXPECT_MATCH(text_, "\\(BuildId: 0123456789abcdef\\)\\nSYMBOLIZE 0123456789abcdef 12345\\n");
 }
+
+TEST_F(TombstoneProtoToTextTest, uid) {
+  ProtoToString();
+  EXPECT_MATCH(text_, "\\nLOG uid: 0\\n");
+}
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index 444c973..9deeeec 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -15,6 +15,15 @@
 // NOTE TO OEMS:
 // If you add custom fields to this proto, do not use numbers in the reserved range.
 
+// NOTE TO CONSUMERS:
+// With proto3 -- unlike proto2 -- HasValue is unreliable for any field
+// where the default value for that type is also a valid value for the field.
+// This means, for example, that a boolean that is false or an integer that
+// is zero will appear to be missing --- but because they're not actually
+// marked as `optional` in this schema, consumers should just use values
+// without first checking whether or not they're "present".
+// https://protobuf.dev/programming-guides/proto3/#default
+
 message CrashDetail {
   bytes name = 1;
   bytes data = 2;
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index 94b2f8c..df5d775 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -79,16 +79,15 @@
   done file by file. Be mindful of wasted space. For example, defining
   **BOARD_IMAGE_PARTITION_RESERVED_SIZE** has a negative impact on the
   right-sizing of images and requires more free dynamic partition space.
-- The kernel requires **CONFIG_OVERLAY_FS=y**. If the kernel version is higher
-  than 4.4, it requires source to be in line with android-common kernels. 
-  The patch series is available on the upstream mailing list and the latest as
-  of Sep 5 2019 is https://www.spinics.net/lists/linux-mtd/msg08331.html
-  This patch adds an override_creds _mount_ option to OverlayFS that
-  permits legacy behavior for systems that do not have overlapping
-  sepolicy rules, principals of least privilege, which is how Android behaves.
-  For 4.19 and higher a rework of the xattr handling to deal with recursion
-  is required. https://patchwork.kernel.org/patch/11117145/ is a start of that
-  adjustment.
+- The kernel requires **CONFIG_OVERLAY_FS=y**. overlayfs is used 'as is' as of
+  android 16, no modifications are required.
+- In order for overlayfs to work, overlays are mounted in the overlay_remounter
+  domain, defined here: system/sepolicy/private/overlay_remounter.te. This domain
+  must have full access to the files on the underlying volumes, add any other file
+  and directory types here
+- For devices with dynamic partitions, we use a simpler logic to decide which
+  partitions to remount, being all logical ones. In case this isn't correct,
+  we added the overlay=on and overlay=off mount flags to allow detailed control.
 - _adb enable-verity_ frees up OverlayFS and reverts the device to the state
   prior to content updates. The update engine performs a full OTA.
 - _adb remount_ overrides are incompatible with OTA resources, so the update
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
index 69d3161..762e70d 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -49,6 +49,10 @@
 #include "fs_mgr_overlayfs_mount.h"
 #include "fs_mgr_priv.h"
 
+// Flag to simplify algorithm for choosing which partitions to overlay to simply overlay
+// all dynamic partitions
+constexpr bool overlay_dynamic_partitions_only = true;
+
 using namespace std::literals;
 using namespace android::fs_mgr;
 using namespace android::storage_literals;
@@ -669,6 +673,19 @@
 
     Fstab candidates;
     for (const auto& entry : fstab) {
+        // fstab overlay flag overrides all other behavior
+        if (entry.fs_mgr_flags.overlay_off) continue;
+        if (entry.fs_mgr_flags.overlay_on) {
+            candidates.push_back(entry);
+            continue;
+        }
+
+        // overlay_dynamic_partitions_only simplifies logic to overlay exactly dynamic partitions
+        if (overlay_dynamic_partitions_only) {
+            if (entry.fs_mgr_flags.logical) candidates.push_back(entry);
+            continue;
+        }
+
         // Filter out partitions whose type doesn't match what's mounted.
         // This avoids spammy behavior on devices which can mount different
         // filesystems for each partition.
diff --git a/fs_mgr/libfstab/fstab.cpp b/fs_mgr/libfstab/fstab.cpp
index 010fbc8..ec23ce5 100644
--- a/fs_mgr/libfstab/fstab.cpp
+++ b/fs_mgr/libfstab/fstab.cpp
@@ -209,6 +209,8 @@
         CheckFlag("metadata_csum", ext_meta_csum);
         CheckFlag("fscompress", fs_compress);
         CheckFlag("overlayfs_remove_missing_lowerdir", overlayfs_remove_missing_lowerdir);
+        CheckFlag("overlay=on", overlay_on);
+        CheckFlag("overlay=off", overlay_off);
 
 #undef CheckFlag
 
diff --git a/fs_mgr/libfstab/include/fstab/fstab.h b/fs_mgr/libfstab/include/fstab/fstab.h
index 0ff3188..4924ae3 100644
--- a/fs_mgr/libfstab/include/fstab/fstab.h
+++ b/fs_mgr/libfstab/include/fstab/fstab.h
@@ -87,6 +87,8 @@
         bool fs_compress : 1;
         bool overlayfs_remove_missing_lowerdir : 1;
         bool is_zoned : 1;
+        bool overlay_on : 1;
+        bool overlay_off : 1;
     } fs_mgr_flags = {};
 
     bool is_encryptable() const { return fs_mgr_flags.crypt; }
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 6f31251..94d8e9f 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -17,7 +17,6 @@
 
 option optimize_for = LITE_RUNTIME;
 
-// Next: 4
 enum SnapshotState {
     // No snapshot is found.
     NONE = 0;
@@ -34,7 +33,6 @@
     MERGE_COMPLETED = 3;
 }
 
-// Next: 3
 enum MergePhase {
     // No merge is in progress.
     NO_MERGE = 0;
@@ -46,7 +44,6 @@
     SECOND_PHASE = 2;
 }
 
-// Next: 13
 message SnapshotStatus {
     // Name of the snapshot. This is usually the name of the snapshotted
     // logical partition; for example, "system_b".
@@ -126,14 +123,11 @@
 
     reserved 18;
 
-    // Blocks size to be verified at once
-    uint64 verify_block_size = 19;
+    reserved 19;
 
-    // Default value is 2, configures threads to do verification phase
-    uint32 num_verify_threads = 20;
+    reserved 20;
 }
 
-// Next: 8
 enum UpdateState {
     // No update or merge is in progress.
     None = 0;
@@ -162,7 +156,6 @@
     Cancelled = 7;
 };
 
-// Next 14:
 //
 // To understand the source of each failure, read snapshot.cpp. To handle new
 // sources of failure, avoid reusing an existing code; add a new code instead.
@@ -190,7 +183,6 @@
     WrongMergeCountConsistencyCheck = 20;
 };
 
-// Next: 8
 message SnapshotUpdateStatus {
     UpdateState state = 1;
 
@@ -235,9 +227,17 @@
 
     // Number of worker threads to serve I/O from dm-user
     uint32 num_worker_threads = 14;
+
+    // Block size to be verified after OTA reboot
+    uint64 verify_block_size = 15;
+
+    // Default value is 3, configures threads to do verification phase
+    uint32 num_verification_threads = 16;
+
+    // Skips verification of partitions
+    bool skip_verification = 17;
 }
 
-// Next: 10
 message SnapshotMergeReport {
     // Status of the update after the merge attempts.
     UpdateState state = 1;
@@ -285,12 +285,12 @@
 }
 
 message VerityHash {
-  // Partition name
-  string partition_name = 1;
+    // Partition name
+    string partition_name = 1;
 
-  // Salt used for verity hashes
-  string salt = 2;
+    // Salt used for verity hashes
+    string salt = 2;
 
-  // sha256 hash values of each block in the image
-  repeated bytes block_hash = 3;
+    // sha256 hash values of each block in the image
+    repeated bytes block_hash = 3;
 }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index e613d54..4520b21 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -429,6 +429,9 @@
     // Pause the snapshot merge.
     bool PauseSnapshotMerge();
 
+    // Resume the snapshot merge.
+    bool ResumeSnapshotMerge();
+
     enum class SnapshotDriver {
         DM_SNAPSHOT,
         DM_USER,
@@ -856,12 +859,21 @@
     // Check if direct reads are enabled for the source image
     bool UpdateUsesODirect(LockedFile* lock);
 
+    // Check if we skip the verification of the target image
+    bool UpdateUsesSkipVerification(LockedFile* lock);
+
     // Get value of maximum cow op merge size
     uint32_t GetUpdateCowOpMergeSize(LockedFile* lock);
 
     // Get number of threads to perform post OTA boot verification
     uint32_t GetUpdateWorkerCount(LockedFile* lock);
 
+    // Get the verification block size
+    uint32_t GetVerificationBlockSize(LockedFile* lock);
+
+    // Get the number of verification threads
+    uint32_t GetNumVerificationThreads(LockedFile* lock);
+
     // Wrapper around libdm, with diagnostics.
     bool DeleteDeviceIfExists(const std::string& name,
                               const std::chrono::milliseconds& timeout_ms = {});
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 8a70400..79443b2 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -64,6 +64,7 @@
   public:
     // Not thread safe.
     static SnapshotMergeStats* GetInstance(SnapshotManager& manager);
+    SnapshotMergeStats(const std::string& path);
 
     // ISnapshotMergeStats overrides
     bool Start() override;
@@ -88,7 +89,6 @@
   private:
     bool ReadState();
     bool DeleteState();
-    SnapshotMergeStats(const std::string& path);
 
     std::string path_;
     SnapshotMergeReport report_;
diff --git a/fs_mgr/libsnapshot/scripts/apply-update.sh b/fs_mgr/libsnapshot/scripts/apply-update.sh
index 2a5a8a2..92bff3b 100755
--- a/fs_mgr/libsnapshot/scripts/apply-update.sh
+++ b/fs_mgr/libsnapshot/scripts/apply-update.sh
@@ -99,6 +99,8 @@
                             Now, the script can safely use this flag for update purpose.
 
   --wipe                   Wipe user data during the update.
+  --boot_snapshot          Boot the device off snapshots - No data wipe is supported
+                              To revert back to original state - `adb shell snapshotctl revert-snapshots`
   --help                   Display this help message.
 
 Environment Variables:
@@ -123,6 +125,7 @@
 }
 
 skip_static_partitions=0
+boot_snapshot=0
 flash_bootloader=1
 wipe_flag=0
 help_flag=0
@@ -139,6 +142,9 @@
     --skip_bootloader)
       flash_bootloader=0
       ;;
+    --boot_snapshot)
+      boot_snapshot=1
+      ;;
     --help)
       help_flag=1
       ;;
@@ -208,7 +214,9 @@
 
 log_message "Applying update"
 
-if (( wipe_flag )); then
+if (( boot_snapshot)); then
+  adb shell snapshotctl map-snapshots $DEVICE_PATH
+elif (( wipe_flag )); then
   adb shell snapshotctl apply-update $DEVICE_PATH -w
 else
   adb shell snapshotctl apply-update $DEVICE_PATH
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index d456197..fa2f569 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -351,9 +351,14 @@
 
     LOG(INFO) << "Removing all update state.";
 
-    if (!RemoveAllSnapshots(lock)) {
-        LOG(ERROR) << "Could not remove all snapshots";
-        return false;
+    if (ReadUpdateState(lock) != UpdateState::None) {
+        // Only call this if we're actually cancelling an update. It's not
+        // expected to yield anything otherwise, and firing up gsid on normal
+        // boot is expensive.
+        if (!RemoveAllSnapshots(lock)) {
+            LOG(ERROR) << "Could not remove all snapshots";
+            return false;
+        }
     }
 
     // It's okay if these fail:
@@ -1787,6 +1792,15 @@
         if (worker_count != 0) {
             snapuserd_argv->emplace_back("-worker_count=" + std::to_string(worker_count));
         }
+        uint32_t verify_block_size = GetVerificationBlockSize(lock.get());
+        if (verify_block_size != 0) {
+            snapuserd_argv->emplace_back("-verify_block_size=" + std::to_string(verify_block_size));
+        }
+        uint32_t num_verify_threads = GetNumVerificationThreads(lock.get());
+        if (num_verify_threads != 0) {
+            snapuserd_argv->emplace_back("-num_verify_threads=" +
+                                         std::to_string(num_verify_threads));
+        }
     }
 
     size_t num_cows = 0;
@@ -2220,6 +2234,11 @@
     return update_status.o_direct();
 }
 
+bool SnapshotManager::UpdateUsesSkipVerification(LockedFile* lock) {
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    return update_status.skip_verification();
+}
+
 uint32_t SnapshotManager::GetUpdateCowOpMergeSize(LockedFile* lock) {
     SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
     return update_status.cow_op_merge_size();
@@ -2230,6 +2249,16 @@
     return update_status.num_worker_threads();
 }
 
+uint32_t SnapshotManager::GetVerificationBlockSize(LockedFile* lock) {
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    return update_status.verify_block_size();
+}
+
+uint32_t SnapshotManager::GetNumVerificationThreads(LockedFile* lock) {
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    return update_status.num_verification_threads();
+}
+
 bool SnapshotManager::MarkSnapuserdFromSystem() {
     auto path = GetSnapuserdFromSystemPath();
 
@@ -3220,8 +3249,11 @@
         status.set_io_uring_enabled(old_status.io_uring_enabled());
         status.set_legacy_snapuserd(old_status.legacy_snapuserd());
         status.set_o_direct(old_status.o_direct());
+        status.set_skip_verification(old_status.skip_verification());
         status.set_cow_op_merge_size(old_status.cow_op_merge_size());
         status.set_num_worker_threads(old_status.num_worker_threads());
+        status.set_verify_block_size(old_status.verify_block_size());
+        status.set_num_verification_threads(old_status.num_verification_threads());
     }
     return WriteSnapshotUpdateStatus(lock, status);
 }
@@ -3600,6 +3632,10 @@
             status.set_o_direct(true);
             LOG(INFO) << "o_direct for source image enabled";
         }
+        if (GetSkipVerificationProperty()) {
+            status.set_skip_verification(true);
+            LOG(INFO) << "skipping verification of images";
+        }
         if (is_legacy_snapuserd) {
             status.set_legacy_snapuserd(true);
             LOG(INFO) << "Setting legacy_snapuserd to true";
@@ -3608,7 +3644,10 @@
                 android::base::GetUintProperty<uint32_t>("ro.virtual_ab.cow_op_merge_size", 0));
         status.set_num_worker_threads(
                 android::base::GetUintProperty<uint32_t>("ro.virtual_ab.num_worker_threads", 0));
-
+        status.set_verify_block_size(
+                android::base::GetUintProperty<uint32_t>("ro.virtual_ab.verify_block_size", 0));
+        status.set_num_verification_threads(
+                android::base::GetUintProperty<uint32_t>("ro.virtual_ab.num_verify_threads", 0));
     } else if (legacy_compression) {
         LOG(INFO) << "Virtual A/B using legacy snapuserd";
     } else {
@@ -4044,8 +4083,11 @@
     ss << "Using userspace snapshots: " << update_status.userspace_snapshots() << std::endl;
     ss << "Using io_uring: " << update_status.io_uring_enabled() << std::endl;
     ss << "Using o_direct: " << update_status.o_direct() << std::endl;
+    ss << "Using skip_verification: " << update_status.skip_verification() << std::endl;
     ss << "Cow op merge size (0 for uncapped): " << update_status.cow_op_merge_size() << std::endl;
     ss << "Worker thread count: " << update_status.num_worker_threads() << std::endl;
+    ss << "Num verification threads: " << update_status.num_verification_threads() << std::endl;
+    ss << "Verify block size: " << update_status.verify_block_size() << std::endl;
     ss << "Using XOR compression: " << GetXorCompressionEnabledProperty() << std::endl;
     ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
     ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
@@ -4696,6 +4738,15 @@
     return false;
 }
 
+bool SnapshotManager::ResumeSnapshotMerge() {
+    auto snapuserd_client = SnapuserdClient::TryConnect(kSnapuserdSocket, 5s);
+    if (snapuserd_client) {
+        // Resume the snapshot-merge
+        return snapuserd_client->ResumeMerge();
+    }
+    return false;
+}
+
 bool SnapshotManager::IsUserspaceSnapshotUpdateInProgress(
         std::vector<std::string>& dynamic_partitions) {
     // We cannot grab /metadata/ota lock here as this
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 8e9d9c5..e684d87 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -24,9 +24,12 @@
 namespace snapshot {
 
 SnapshotMergeStats* SnapshotMergeStats::GetInstance(SnapshotManager& parent) {
-    static SnapshotMergeStats g_instance(parent.GetMergeStateFilePath());
-    CHECK_EQ(g_instance.path_, parent.GetMergeStateFilePath());
-    return &g_instance;
+    static std::unique_ptr<SnapshotMergeStats> g_instance;
+
+    if (!g_instance || g_instance->path_ != parent.GetMergeStateFilePath()) {
+        g_instance = std::make_unique<SnapshotMergeStats>(parent.GetMergeStateFilePath());
+    }
+    return g_instance.get();
 }
 
 SnapshotMergeStats::SnapshotMergeStats(const std::string& path) : path_(path), running_(false) {}
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index e1a3310..32c8e37 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -83,6 +83,10 @@
                  "    Deprecated.\n"
                  "  map\n"
                  "    Map all partitions at /dev/block/mapper\n"
+                 "  pause-merge\n"
+                 "    Pause snapshot merge\n"
+                 "  resume-merge\n"
+                 "    Resume snapshot merge\n"
                  "  map-snapshots <directory where snapshot patches are present>\n"
                  "    Map all snapshots based on patches present in the directory\n"
                  "  unmap-snapshots\n"
@@ -539,6 +543,16 @@
     return SnapshotManager::New()->UnmapAllSnapshots();
 }
 
+bool PauseSnapshotMerge(int, char** argv) {
+    android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
+    return SnapshotManager::New()->PauseSnapshotMerge();
+}
+
+bool ResumeSnapshotMerge(int, char** argv) {
+    android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
+    return SnapshotManager::New()->ResumeSnapshotMerge();
+}
+
 bool MergeCmdHandler(int /*argc*/, char** argv) {
     android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
     LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead.";
@@ -1088,6 +1102,8 @@
         {"dump-verity-hash", DumpVerityHash},
 #endif
         {"unmap", UnmapCmdHandler},
+        {"pause-merge", PauseSnapshotMerge},
+        {"resume-merge", ResumeSnapshotMerge},
         // clang-format on
 };
 
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 639116e..9972bc7 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -88,6 +88,7 @@
         "libprocessgroup",
         "libprocessgroup_util",
         "libjsoncpp",
+        "liburing_cpp",
     ],
     export_include_dirs: ["include"],
     header_libs: [
@@ -136,6 +137,7 @@
         "libext4_utils",
         "liburing",
         "libzstd",
+        "liburing_cpp",
     ],
 
     header_libs: [
@@ -222,6 +224,7 @@
         "libjsoncpp",
         "liburing",
         "libz",
+        "liburing_cpp",
     ],
     include_dirs: [
         ".",
@@ -319,6 +322,7 @@
         "libjsoncpp",
         "liburing",
         "libz",
+        "liburing_cpp",
     ],
     include_dirs: [
         ".",
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index 453627c..39850c0 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
@@ -111,6 +111,9 @@
 
     // Pause Merge threads
     bool PauseMerge();
+
+    // Resume Merge threads
+    bool ResumeMerge();
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 0497c65..693fe39 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -395,7 +395,17 @@
         LOG(ERROR) << "Failed to pause snapshot merge.";
         return false;
     }
-    return true;
+    std::string response = Receivemsg();
+    return response == "success";
+}
+
+bool SnapuserdClient::ResumeMerge() {
+    if (!Sendmsg("resume_merge")) {
+        LOG(ERROR) << "Failed to resume snapshot merge.";
+        return false;
+    }
+    std::string response = Receivemsg();
+    return response == "success";
 }
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index 32e16cc..d29223e 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -20,8 +20,13 @@
 #include <gflags/gflags.h>
 #include <snapuserd/snapuserd_client.h>
 
+#include <storage_literals/storage_literals.h>
+#include "user-space-merge/snapuserd_core.h"
+
 #include "snapuserd_daemon.h"
 
+using namespace android::storage_literals;
+
 DEFINE_string(socket, android::snapshot::kSnapuserdSocket, "Named socket or socket path.");
 DEFINE_bool(no_socket, false,
             "If true, no socket is used. Each additional argument is an INIT message.");
@@ -30,8 +35,12 @@
 DEFINE_bool(user_snapshot, false, "If true, user-space snapshots are used");
 DEFINE_bool(io_uring, false, "If true, io_uring feature is enabled");
 DEFINE_bool(o_direct, false, "If true, enable direct reads on source device");
+DEFINE_bool(skip_verification, false, "If true, skip verification of partitions");
 DEFINE_int32(cow_op_merge_size, 0, "number of operations to be processed at once");
-DEFINE_int32(worker_count, 4, "number of worker threads used to serve I/O requests to dm-user");
+DEFINE_int32(worker_count, android::snapshot::kNumWorkerThreads,
+             "number of worker threads used to serve I/O requests to dm-user");
+DEFINE_int32(verify_block_size, 1_MiB, "block sized used during verification of snapshots");
+DEFINE_int32(num_verify_threads, 3, "number of threads used during verification phase");
 
 namespace android {
 namespace snapshot {
@@ -95,9 +104,6 @@
     MaskAllSignalsExceptIntAndTerm();
 
     user_server_.SetServerRunning();
-    if (FLAGS_io_uring) {
-        user_server_.SetIouringEnabled();
-    }
 
     if (FLAGS_socket_handoff) {
         return user_server_.RunForSocketHandoff();
@@ -110,14 +116,20 @@
     }
     for (int i = arg_start; i < argc; i++) {
         auto parts = android::base::Split(argv[i], ",");
-
         if (parts.size() != 4) {
             LOG(ERROR) << "Malformed message, expected at least four sub-arguments.";
             return false;
         }
-        auto handler =
-                user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3], FLAGS_worker_count,
-                                        FLAGS_o_direct, FLAGS_cow_op_merge_size);
+        HandlerOptions options = {
+                .num_worker_threads = FLAGS_worker_count,
+                .use_iouring = FLAGS_io_uring,
+                .o_direct = FLAGS_o_direct,
+                .skip_verification = FLAGS_skip_verification,
+                .cow_op_merge_size = static_cast<uint32_t>(FLAGS_cow_op_merge_size),
+                .verify_block_size = static_cast<uint32_t>(FLAGS_verify_block_size),
+                .num_verification_threads = static_cast<uint32_t>(FLAGS_num_verify_threads),
+        };
+        auto handler = user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3], options);
         if (!handler || !user_server_.StartHandler(parts[0])) {
             return false;
         }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
index ef4ba93..c15ac6b 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
@@ -40,8 +40,9 @@
 
 bool Extractor::Init() {
     auto opener = factory_.CreateTestOpener(control_name_);
+    HandlerOptions options;
     handler_ = std::make_shared<SnapshotHandler>(control_name_, cow_path_, base_path_, base_path_,
-                                                 opener, 1, false, false, false, 0);
+                                                 opener, options);
     if (!handler_->InitCowDevice()) {
         return false;
     }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h
index 65285b1..814bc85 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h
@@ -14,8 +14,8 @@
 
 #pragma once
 
+#include <future>
 #include <string>
-#include <thread>
 
 #include <android-base/unique_fd.h>
 #include "merge_worker.h"
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
index cf507e3..6b6f071 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
@@ -52,11 +52,9 @@
 std::shared_ptr<HandlerThread> SnapshotHandlerManager::AddHandler(
         const std::string& misc_name, const std::string& cow_device_path,
         const std::string& backing_device, const std::string& base_path_merge,
-        std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,
-        bool o_direct, uint32_t cow_op_merge_size) {
-    auto snapuserd = std::make_shared<SnapshotHandler>(
-            misc_name, cow_device_path, backing_device, base_path_merge, opener, num_worker_threads,
-            use_iouring, perform_verification_, o_direct, cow_op_merge_size);
+        std::shared_ptr<IBlockServerOpener> opener, HandlerOptions options) {
+    auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
+                                                       base_path_merge, opener, options);
     if (!snapuserd->InitCowDevice()) {
         LOG(ERROR) << "Failed to initialize Snapuserd";
         return nullptr;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
index 89f3461..d10d8e8 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
@@ -27,6 +27,16 @@
 namespace android {
 namespace snapshot {
 
+struct HandlerOptions {
+    int num_worker_threads{};
+    bool use_iouring{};
+    bool o_direct{};
+    bool skip_verification{};
+    uint32_t cow_op_merge_size{};
+    uint32_t verify_block_size{};
+    uint32_t num_verification_threads{};
+};
+
 class SnapshotHandler;
 
 class HandlerThread {
@@ -53,11 +63,12 @@
     virtual ~ISnapshotHandlerManager() {}
 
     // Add a new snapshot handler but do not start serving requests yet.
-    virtual std::shared_ptr<HandlerThread> AddHandler(
-            const std::string& misc_name, const std::string& cow_device_path,
-            const std::string& backing_device, const std::string& base_path_merge,
-            std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,
-            bool o_direct, uint32_t cow_op_merge_size) = 0;
+    virtual std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name,
+                                                      const std::string& cow_device_path,
+                                                      const std::string& backing_device,
+                                                      const std::string& base_path_merge,
+                                                      std::shared_ptr<IBlockServerOpener> opener,
+                                                      HandlerOptions options) = 0;
 
     // Start serving requests on a snapshot handler.
     virtual bool StartHandler(const std::string& misc_name) = 0;
@@ -102,8 +113,8 @@
                                               const std::string& backing_device,
                                               const std::string& base_path_merge,
                                               std::shared_ptr<IBlockServerOpener> opener,
-                                              int num_worker_threads, bool use_iouring,
-                                              bool o_direct, uint32_t cow_op_merge_size) override;
+                                              HandlerOptions options) override;
+
     bool StartHandler(const std::string& misc_name) override;
     bool DeleteHandler(const std::string& misc_name) override;
     bool InitiateMerge(const std::string& misc_name) override;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 7c9a64e..1f3d3a0 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -22,6 +22,8 @@
 #include <android-base/strings.h>
 #include <snapuserd/dm_user_block_server.h>
 
+#include <future>
+
 #include "merge_worker.h"
 #include "read_worker.h"
 #include "utility.h"
@@ -35,26 +37,21 @@
 
 SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
                                  std::string backing_device, std::string base_path_merge,
-                                 std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads,
-                                 bool use_iouring, bool perform_verification, bool o_direct,
-                                 uint32_t cow_op_merge_size) {
+                                 std::shared_ptr<IBlockServerOpener> opener,
+                                 HandlerOptions options) {
     misc_name_ = std::move(misc_name);
     cow_device_ = std::move(cow_device);
     backing_store_device_ = std::move(backing_device);
     block_server_opener_ = std::move(opener);
     base_path_merge_ = std::move(base_path_merge);
-    num_worker_threads_ = num_worker_threads;
-    is_io_uring_enabled_ = use_iouring;
-    perform_verification_ = perform_verification;
-    o_direct_ = o_direct;
-    cow_op_merge_size_ = cow_op_merge_size;
+    handler_options_ = options;
 }
 
 bool SnapshotHandler::InitializeWorkers() {
     for (int i = 0; i < num_worker_threads_; i++) {
         auto wt = std::make_unique<ReadWorker>(cow_device_, backing_store_device_, misc_name_,
                                                base_path_merge_, GetSharedPtr(),
-                                               block_server_opener_, o_direct_);
+                                               block_server_opener_, handler_options_.o_direct);
         if (!wt->Init()) {
             SNAP_LOG(ERROR) << "Thread initialization failed";
             return false;
@@ -62,13 +59,16 @@
 
         worker_threads_.push_back(std::move(wt));
     }
-    merge_thread_ = std::make_unique<MergeWorker>(cow_device_, misc_name_, base_path_merge_,
-                                                  GetSharedPtr(), cow_op_merge_size_);
+    merge_thread_ =
+            std::make_unique<MergeWorker>(cow_device_, misc_name_, base_path_merge_, GetSharedPtr(),
+                                          handler_options_.cow_op_merge_size);
 
-    read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
-                                                     GetSharedPtr(), cow_op_merge_size_);
+    read_ahead_thread_ =
+            std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
+                                        GetSharedPtr(), handler_options_.cow_op_merge_size);
 
-    update_verify_ = std::make_unique<UpdateVerify>(misc_name_);
+    update_verify_ = std::make_unique<UpdateVerify>(misc_name_, handler_options_.verify_block_size,
+                                                    handler_options_.num_verification_threads);
 
     return true;
 }
@@ -429,7 +429,7 @@
     // During selinux init transition, libsnapshot will propagate the
     // status of io_uring enablement. As properties are not initialized,
     // we cannot query system property.
-    if (is_io_uring_enabled_) {
+    if (handler_options_.use_iouring) {
         return true;
     }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index 924539f..9c5d58b 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -24,15 +24,11 @@
 
 #include <condition_variable>
 #include <cstring>
-#include <future>
 #include <iostream>
-#include <limits>
 #include <mutex>
 #include <ostream>
 #include <string>
-#include <thread>
 #include <unordered_map>
-#include <unordered_set>
 #include <vector>
 
 #include <android-base/file.h>
@@ -48,6 +44,7 @@
 #include <snapuserd/snapuserd_kernel.h>
 #include <storage_literals/storage_literals.h>
 #include <system/thread_defs.h>
+#include <user-space-merge/handler_manager.h>
 #include "snapuserd_readahead.h"
 #include "snapuserd_verify.h"
 
@@ -104,8 +101,7 @@
   public:
     SnapshotHandler(std::string misc_name, std::string cow_device, std::string backing_device,
                     std::string base_path_merge, std::shared_ptr<IBlockServerOpener> opener,
-                    int num_workers, bool use_iouring, bool perform_verification, bool o_direct,
-                    uint32_t cow_op_merge_size);
+                    HandlerOptions options);
     bool InitCowDevice();
     bool Start();
 
@@ -248,14 +244,12 @@
     bool merge_initiated_ = false;
     bool merge_monitored_ = false;
     bool attached_ = false;
-    bool is_io_uring_enabled_ = false;
     bool scratch_space_ = false;
     int num_worker_threads_ = kNumWorkerThreads;
     bool perform_verification_ = true;
     bool resume_merge_ = false;
     bool merge_complete_ = false;
-    bool o_direct_ = false;
-    uint32_t cow_op_merge_size_ = 0;
+    HandlerOptions handler_options_;
     std::unique_ptr<UpdateVerify> update_verify_;
     std::shared_ptr<IBlockServerOpener> block_server_opener_;
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index a40617b..b21189c 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -35,6 +35,7 @@
 #include <snapuserd/dm_user_block_server.h>
 #include <snapuserd/snapuserd_client.h>
 #include "snapuserd_server.h"
+#include "user-space-merge/handler_manager.h"
 #include "user-space-merge/snapuserd_core.h"
 
 namespace android {
@@ -126,7 +127,8 @@
             return Sendmsg(fd, "fail");
         }
 
-        auto handler = AddHandler(out[1], out[2], out[3], out[4], std::nullopt);
+        HandlerOptions options;
+        auto handler = AddHandler(out[1], out[2], out[3], out[4], options);
         if (!handler) {
             return Sendmsg(fd, "fail");
         }
@@ -229,7 +231,10 @@
         return Sendmsg(fd, "success");
     } else if (cmd == "pause_merge") {
         handlers_->PauseMerge();
-        return true;
+        return Sendmsg(fd, "success");
+    } else if (cmd == "resume_merge") {
+        handlers_->ResumeMerge();
+        return Sendmsg(fd, "success");
     } else {
         LOG(ERROR) << "Received unknown message type from client";
         Sendmsg(fd, "fail");
@@ -345,11 +350,11 @@
     SetTerminating();
 }
 
-std::shared_ptr<HandlerThread> UserSnapshotServer::AddHandler(
-        const std::string& misc_name, const std::string& cow_device_path,
-        const std::string& backing_device, const std::string& base_path_merge,
-        std::optional<uint32_t> num_worker_threads, const bool o_direct,
-        uint32_t cow_op_merge_size) {
+std::shared_ptr<HandlerThread> UserSnapshotServer::AddHandler(const std::string& misc_name,
+                                                              const std::string& cow_device_path,
+                                                              const std::string& backing_device,
+                                                              const std::string& base_path_merge,
+                                                              HandlerOptions options) {
     // We will need multiple worker threads only during
     // device boot after OTA. For all other purposes,
     // one thread is sufficient. We don't want to consume
@@ -358,23 +363,19 @@
     //
     // During boot up, we need multiple threads primarily for
     // update-verification.
-    if (!num_worker_threads.has_value()) {
-        num_worker_threads = kNumWorkerThreads;
-    }
     if (is_socket_present_) {
-        num_worker_threads = 1;
+        options.num_worker_threads = 1;
     }
 
-    if (android::base::EndsWith(misc_name, "-init") || is_socket_present_ ||
-        (access(kBootSnapshotsWithoutSlotSwitch, F_OK) == 0)) {
+    if (options.skip_verification || android::base::EndsWith(misc_name, "-init") ||
+        is_socket_present_ || (access(kBootSnapshotsWithoutSlotSwitch, F_OK) == 0)) {
         handlers_->DisableVerification();
     }
 
     auto opener = block_server_factory_->CreateOpener(misc_name);
 
     return handlers_->AddHandler(misc_name, cow_device_path, backing_device, base_path_merge,
-                                 opener, num_worker_threads.value(), io_uring_enabled_, o_direct,
-                                 cow_op_merge_size);
+                                 opener, options);
 }
 
 bool UserSnapshotServer::WaitForSocket() {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index f002e8d..73ce7b2 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -51,7 +51,6 @@
     std::vector<struct pollfd> watched_fds_;
     bool is_socket_present_ = false;
     bool is_server_running_ = false;
-    bool io_uring_enabled_ = false;
     std::unique_ptr<ISnapshotHandlerManager> handlers_;
     std::unique_ptr<IBlockServerFactory> block_server_factory_;
 
@@ -87,17 +86,13 @@
                                               const std::string& cow_device_path,
                                               const std::string& backing_device,
                                               const std::string& base_path_merge,
-                                              std::optional<uint32_t> num_worker_threads,
-                                              bool o_direct = false,
-                                              uint32_t cow_op_merge_size = 0);
+                                              HandlerOptions options);
     bool StartHandler(const std::string& misc_name);
 
     void SetTerminating() { terminating_ = true; }
     void ReceivedSocketSignal() { received_socket_signal_ = true; }
     void SetServerRunning() { is_server_running_ = true; }
     bool IsServerRunning() { return is_server_running_; }
-    void SetIouringEnabled() { io_uring_enabled_ = true; }
-    bool IsIouringEnabled() { return io_uring_enabled_; }
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index 0790a19..f3795a1 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -24,9 +24,8 @@
 #include <unistd.h>
 
 #include <chrono>
-#include <iostream>
+#include <future>
 #include <memory>
-#include <string_view>
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
@@ -44,7 +43,6 @@
 #include "snapuserd_core.h"
 #include "testing/dm_user_harness.h"
 #include "testing/host_harness.h"
-#include "testing/temp_device.h"
 #include "utility.h"
 
 namespace android {
@@ -68,6 +66,8 @@
     int block_size;
     int num_threads;
     uint32_t cow_op_merge_size;
+    uint32_t verification_block_size;
+    uint32_t num_verification_threads;
 };
 
 class SnapuserdTestBase : public ::testing::TestWithParam<TestParam> {
@@ -731,9 +731,17 @@
     auto opener = factory->CreateOpener(system_device_ctrl_name_);
     handlers_->DisableVerification();
     const TestParam params = GetParam();
-    auto handler = handlers_->AddHandler(
-            system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(), base_dev_->GetPath(),
-            opener, 1, params.io_uring, params.o_direct, params.cow_op_merge_size);
+    HandlerOptions options = {
+            .num_worker_threads = params.num_threads,
+            .use_iouring = params.io_uring,
+            .o_direct = params.o_direct,
+            .cow_op_merge_size = params.cow_op_merge_size,
+            .verify_block_size = params.verification_block_size,
+            .num_verification_threads = params.num_verification_threads,
+    };
+    auto handler =
+            handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(),
+                                  base_dev_->GetPath(), opener, options);
     ASSERT_NE(handler, nullptr);
     ASSERT_NE(handler->snapuserd(), nullptr);
 #ifdef __ANDROID__
@@ -1273,9 +1281,17 @@
     ASSERT_NE(opener_, nullptr);
 
     const TestParam params = GetParam();
-    handler_ = std::make_shared<SnapshotHandler>(
-            system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(), base_dev_->GetPath(),
-            opener_, 1, false, false, params.o_direct, params.cow_op_merge_size);
+    HandlerOptions options = {
+            .num_worker_threads = params.num_threads,
+            .use_iouring = params.io_uring,
+            .o_direct = params.o_direct,
+            .cow_op_merge_size = params.cow_op_merge_size,
+            .verify_block_size = params.verification_block_size,
+            .num_verification_threads = params.num_verification_threads,
+    };
+    handler_ = std::make_shared<SnapshotHandler>(system_device_ctrl_name_, cow_system_->path,
+                                                 base_dev_->GetPath(), base_dev_->GetPath(),
+                                                 opener_, options);
     ASSERT_TRUE(handler_->InitCowDevice());
     ASSERT_TRUE(handler_->InitializeWorkers());
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
index 957c6a8..2dfcc36 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
@@ -20,8 +20,10 @@
 #include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 
-#include "android-base/properties.h"
+#include <future>
+
 #include "snapuserd_core.h"
+#include "utility.h"
 
 namespace android {
 namespace snapshot {
@@ -30,8 +32,12 @@
 using namespace android::dm;
 using android::base::unique_fd;
 
-UpdateVerify::UpdateVerify(const std::string& misc_name)
-    : misc_name_(misc_name), state_(UpdateVerifyState::VERIFY_UNKNOWN) {}
+UpdateVerify::UpdateVerify(const std::string& misc_name, uint32_t verify_block_size,
+                           uint32_t num_verification_threads)
+    : misc_name_(misc_name),
+      state_(UpdateVerifyState::VERIFY_UNKNOWN),
+      verify_block_size_(verify_block_size),
+      num_verification_threads_(num_verification_threads) {}
 
 bool UpdateVerify::CheckPartitionVerification() {
     auto now = std::chrono::system_clock::now();
@@ -104,43 +110,107 @@
         return false;
     }
 
-    loff_t file_offset = offset;
-    auto verify_block_size = android::base::GetUintProperty<uint>("ro.virtual_ab.verify_block_size",
-                                                                  kBlockSizeVerify);
-    const uint64_t read_sz = verify_block_size;
+    int queue_depth = std::max(queue_depth_, 1);
+    int verify_block_size = verify_block_size_;
 
-    void* addr;
-    ssize_t page_size = getpagesize();
-    if (posix_memalign(&addr, page_size, read_sz) < 0) {
-        SNAP_PLOG(ERROR) << "posix_memalign failed "
-                         << " page_size: " << page_size << " read_sz: " << read_sz;
+    // Smaller partitions don't need a bigger queue-depth.
+    // This is required for low-memory devices.
+    if (dev_sz < threshold_size_) {
+        queue_depth = std::max(queue_depth / 2, 1);
+        verify_block_size >>= 2;
+    }
+
+    if (!IsBlockAligned(verify_block_size)) {
+        verify_block_size = EXT4_ALIGN(verify_block_size, BLOCK_SZ);
+    }
+
+    std::unique_ptr<io_uring_cpp::IoUringInterface> ring =
+            io_uring_cpp::IoUringInterface::CreateLinuxIoUring(queue_depth, 0);
+    if (ring.get() == nullptr) {
+        PLOG(ERROR) << "Verify: io_uring_queue_init failed for queue_depth: " << queue_depth;
         return false;
     }
 
-    std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
-
-    uint64_t bytes_read = 0;
-
-    while (true) {
-        size_t to_read = std::min((dev_sz - file_offset), read_sz);
-
-        if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
-            SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
-                             << " partition-name: " << partition_name
-                             << " at offset: " << file_offset << " read-size: " << to_read
-                             << " block-size: " << dev_sz;
+    std::unique_ptr<struct iovec[]> vecs = std::make_unique<struct iovec[]>(queue_depth);
+    std::vector<std::unique_ptr<void, decltype(&::free)>> buffers;
+    for (int i = 0; i < queue_depth; i++) {
+        void* addr;
+        ssize_t page_size = getpagesize();
+        if (posix_memalign(&addr, page_size, verify_block_size) < 0) {
+            LOG(ERROR) << "posix_memalign failed";
             return false;
         }
 
-        bytes_read += to_read;
-        file_offset += (skip_blocks * verify_block_size);
-        if (file_offset >= dev_sz) {
+        buffers.emplace_back(addr, ::free);
+        vecs[i].iov_base = addr;
+        vecs[i].iov_len = verify_block_size;
+    }
+
+    auto ret = ring->RegisterBuffers(vecs.get(), queue_depth);
+    if (!ret.IsOk()) {
+        SNAP_LOG(ERROR) << "io_uring_register_buffers failed: " << ret.ErrCode();
+        return false;
+    }
+
+    loff_t file_offset = offset;
+    const uint64_t read_sz = verify_block_size;
+    uint64_t total_read = 0;
+    int num_submitted = 0;
+
+    SNAP_LOG(DEBUG) << "VerifyBlocks: queue_depth: " << queue_depth
+                    << " verify_block_size: " << verify_block_size << " dev_sz: " << dev_sz
+                    << " file_offset: " << file_offset << " skip_blocks: " << skip_blocks;
+
+    while (file_offset < dev_sz) {
+        for (size_t i = 0; i < queue_depth; i++) {
+            uint64_t to_read = std::min((dev_sz - file_offset), read_sz);
+            if (to_read <= 0) break;
+
+            const auto sqe =
+                    ring->PrepReadFixed(fd.get(), vecs[i].iov_base, to_read, file_offset, i);
+            if (!sqe.IsOk()) {
+                SNAP_PLOG(ERROR) << "PrepReadFixed failed";
+                return false;
+            }
+            file_offset += (skip_blocks * to_read);
+            total_read += to_read;
+            num_submitted += 1;
+            if (file_offset >= dev_sz) {
+                break;
+            }
+        }
+
+        if (num_submitted == 0) {
             break;
         }
+
+        const auto io_submit = ring->SubmitAndWait(num_submitted);
+        if (!io_submit.IsOk()) {
+            SNAP_LOG(ERROR) << "SubmitAndWait failed: " << io_submit.ErrMsg()
+                            << " for: " << num_submitted << " entries.";
+            return false;
+        }
+
+        SNAP_LOG(DEBUG) << "io_uring_submit: " << total_read << "num_submitted: " << num_submitted
+                        << "ret: " << ret;
+
+        const auto cqes = ring->PopCQE(num_submitted);
+        if (cqes.IsErr()) {
+            SNAP_LOG(ERROR) << "PopCqe failed for: " << num_submitted
+                            << " error: " << cqes.GetError().ErrMsg();
+            return false;
+        }
+        for (const auto& cqe : cqes.GetResult()) {
+            if (cqe.res < 0) {
+                SNAP_LOG(ERROR) << "I/O failed: cqe->res: " << cqe.res;
+                return false;
+            }
+            num_submitted -= 1;
+        }
     }
 
-    SNAP_LOG(DEBUG) << "Verification success with bytes-read: " << bytes_read
-                    << " dev_sz: " << dev_sz << " partition_name: " << partition_name;
+    SNAP_LOG(DEBUG) << "Verification success with io_uring: " << " dev_sz: " << dev_sz
+                    << " partition_name: " << partition_name << " total_read: " << total_read;
 
     return true;
 }
@@ -175,35 +245,29 @@
         return false;
     }
 
-    /*
-     * Not all partitions are of same size. Some partitions are as small as
-     * 100Mb. We can just finish them in a single thread. For bigger partitions
-     * such as product, 4 threads are sufficient enough.
-     *
-     * TODO: With io_uring SQ_POLL support, we can completely cut this
-     * down to just single thread for all partitions and potentially verify all
-     * the partitions with zero syscalls. Additionally, since block layer
-     * supports polling, IO_POLL could be used which will further cut down
-     * latency.
-     */
+    if (!KernelSupportsIoUring()) {
+        SNAP_LOG(INFO) << "Kernel does not support io_uring. Skipping verification.\n";
+        // This will fallback to update_verifier to do the verification.
+        return false;
+    }
+
     int num_threads = kMinThreadsToVerify;
-    auto verify_threshold_size = android::base::GetUintProperty<uint>(
-            "ro.virtual_ab.verify_threshold_size", kThresholdSize);
-    if (dev_sz > verify_threshold_size) {
+    if (dev_sz > threshold_size_) {
         num_threads = kMaxThreadsToVerify;
+        if (num_verification_threads_ != 0) {
+            num_threads = num_verification_threads_;
+        }
     }
 
     std::vector<std::future<bool>> threads;
     off_t start_offset = 0;
     const int skip_blocks = num_threads;
 
-    auto verify_block_size =
-            android::base::GetUintProperty("ro.virtual_ab.verify_block_size", kBlockSizeVerify);
     while (num_threads) {
         threads.emplace_back(std::async(std::launch::async, &UpdateVerify::VerifyBlocks, this,
                                         partition_name, dm_block_device, start_offset, skip_blocks,
                                         dev_sz));
-        start_offset += verify_block_size;
+        start_offset += verify_block_size_;
         num_threads -= 1;
         if (start_offset >= dev_sz) {
             break;
@@ -218,9 +282,9 @@
     if (ret) {
         succeeded = true;
         UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_SUCCESS);
-        SNAP_LOG(INFO) << "Partition: " << partition_name << " Block-device: " << dm_block_device
-                       << " Size: " << dev_sz
-                       << " verification success. Duration : " << timer.duration().count() << " ms";
+        SNAP_LOG(INFO) << "Partition verification success: " << partition_name
+                       << " Block-device: " << dm_block_device << " Size: " << dev_sz
+                       << " Duration : " << timer.duration().count() << " ms";
         return true;
     }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
index b300a70..f995c7f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
@@ -15,6 +15,7 @@
 
 #pragma once
 
+#include <liburing.h>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -22,6 +23,7 @@
 #include <mutex>
 #include <string>
 
+#include <liburing_cpp/IoUring.h>
 #include <snapuserd/snapuserd_kernel.h>
 #include <storage_literals/storage_literals.h>
 
@@ -32,7 +34,8 @@
 
 class UpdateVerify {
   public:
-    UpdateVerify(const std::string& misc_name);
+    UpdateVerify(const std::string& misc_name, uint32_t verify_block_size,
+                 uint32_t num_verification_threads);
     void VerifyUpdatePartition();
     bool CheckPartitionVerification();
 
@@ -48,27 +51,24 @@
     std::mutex m_lock_;
     std::condition_variable m_cv_;
 
+    int kMinThreadsToVerify = 1;
+    int kMaxThreadsToVerify = 3;
+
     /*
-     * Scanning of partitions is an expensive operation both in terms of memory
-     * and CPU usage. The goal here is to scan the partitions fast enough without
-     * significant increase in the boot time.
-     *
-     * Partitions such as system, product which may be huge and may need multiple
-     * threads to speed up the verification process. Using multiple threads for
-     * all partitions may increase CPU usage significantly. Hence, limit that to
-     * 1 thread per partition.
+     * To optimize partition scanning speed without significantly impacting boot time,
+     * we employ O_DIRECT, bypassing the page-cache. However, O_DIRECT's memory
+     * allocation from CMA can be problematic on devices with restricted CMA space.
+     * To address this, io_uring_register_buffers() pre-registers I/O buffers,
+     * preventing CMA usage. See b/401952955 for more details.
      *
      * These numbers were derived by monitoring the memory and CPU pressure
      * (/proc/pressure/{cpu,memory}; and monitoring the Inactive(file) and
      * Active(file) pages from /proc/meminfo.
-     *
-     * Additionally, for low memory devices, it is advisable to use O_DIRECT
-     * functionality for source block device.
      */
-    int kMinThreadsToVerify = 1;
-    int kMaxThreadsToVerify = 3;
-    uint64_t kThresholdSize = 750_MiB;
-    uint64_t kBlockSizeVerify = 2_MiB;
+    uint64_t verify_block_size_ = 1_MiB;
+    uint64_t threshold_size_ = 2_GiB;
+    uint32_t num_verification_threads_;
+    int queue_depth_ = 4;
 
     bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
     void UpdatePartitionVerificationState(UpdateVerifyState state);
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 30510d0..04ee069 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -199,7 +199,7 @@
 }
 
 std::ostream& operator<<(std::ostream& os, const Now&) {
-    struct tm now{};
+    struct tm now {};
     time_t t = time(nullptr);
     localtime_r(&t, &now);
     return os << std::put_time(&now, "%Y%m%d-%H%M%S");
@@ -293,6 +293,11 @@
     return fetcher->GetBoolProperty("ro.virtual_ab.o_direct.enabled", false);
 }
 
+bool GetSkipVerificationProperty() {
+    auto fetcher = IPropertyFetcher::GetInstance();
+    return fetcher->GetBoolProperty("ro.virtual_ab.skip_verification", false);
+}
+
 std::string GetOtherPartitionName(const std::string& name) {
     auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
     CHECK(suffix == "_a" || suffix == "_b");
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 30c75c0..eaf51c1 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -136,6 +136,7 @@
 bool GetIouringEnabledProperty();
 bool GetXorCompressionEnabledProperty();
 bool GetODirectEnabledProperty();
+bool GetSkipVerificationProperty();
 
 bool CanUseUserspaceSnapshots();
 bool IsDmSnapshotTestingEnabled();
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index b0a14bb..64c85e2 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -342,9 +342,10 @@
     return value;
 }
 
-static int getIntField(const String8& path) {
+template <typename T = int>
+static T getIntField(const String8& path) {
     std::string buf;
-    int value = 0;
+    T value = 0;
 
     if (readFromFile(path, &buf) > 0)
         android::base::ParseInt(buf, &value);
@@ -416,11 +417,11 @@
 
     if (!mHealthdConfig->batteryManufacturingDatePath.empty())
         ensureBatteryHealthData(mHealthInfo.get())->batteryManufacturingDateSeconds =
-                getIntField(mHealthdConfig->batteryManufacturingDatePath);
+                getIntField<int64_t>(mHealthdConfig->batteryManufacturingDatePath);
 
     if (!mHealthdConfig->batteryFirstUsageDatePath.empty())
         ensureBatteryHealthData(mHealthInfo.get())->batteryFirstUsageSeconds =
-                getIntField(mHealthdConfig->batteryFirstUsageDatePath);
+                getIntField<int64_t>(mHealthdConfig->batteryFirstUsageDatePath);
 
     mHealthInfo->batteryTemperatureTenthsCelsius =
             mBatteryFixedTemperature ? mBatteryFixedTemperature
@@ -715,49 +716,54 @@
     char vs[128];
     const HealthInfo& props = *mHealthInfo;
 
+    snprintf(vs, sizeof(vs), "Cached HealthInfo:\n");
+    write(fd, vs, strlen(vs));
     snprintf(vs, sizeof(vs),
-             "ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\n",
+             "  ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\n",
              props.chargerAcOnline, props.chargerUsbOnline, props.chargerWirelessOnline,
              props.chargerDockOnline, props.maxChargingCurrentMicroamps,
              props.maxChargingVoltageMicrovolts);
     write(fd, vs, strlen(vs));
-    snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
+    snprintf(vs, sizeof(vs), "  status: %d health: %d present: %d\n",
              props.batteryStatus, props.batteryHealth, props.batteryPresent);
     write(fd, vs, strlen(vs));
-    snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n", props.batteryLevel,
+    snprintf(vs, sizeof(vs), "  level: %d voltage: %d temp: %d\n", props.batteryLevel,
              props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
     write(fd, vs, strlen(vs));
 
     if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
+        snprintf(vs, sizeof(vs), "  current now: %d\n", props.batteryCurrentMicroamps);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryCycleCountPath.empty()) {
+        snprintf(vs, sizeof(vs), "  cycle count: %d\n", props.batteryCycleCount);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryFullChargePath.empty()) {
+        snprintf(vs, sizeof(vs), "  Full charge: %d\n", props.batteryFullChargeUah);
+        write(fd, vs, strlen(vs));
+    }
+
+    snprintf(vs, sizeof(vs), "Real-time Values:\n");
+    write(fd, vs, strlen(vs));
+
+    if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
         v = getIntField(mHealthdConfig->batteryCurrentNowPath);
-        snprintf(vs, sizeof(vs), "current now: %d\n", v);
+        snprintf(vs, sizeof(vs), "  current now: %d\n", v);
         write(fd, vs, strlen(vs));
     }
 
     if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
         v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
-        snprintf(vs, sizeof(vs), "current avg: %d\n", v);
+        snprintf(vs, sizeof(vs), "  current avg: %d\n", v);
         write(fd, vs, strlen(vs));
     }
 
     if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
         v = getIntField(mHealthdConfig->batteryChargeCounterPath);
-        snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
-        write(fd, vs, strlen(vs));
-    }
-
-    if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
-        snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
-        write(fd, vs, strlen(vs));
-    }
-
-    if (!mHealthdConfig->batteryCycleCountPath.empty()) {
-        snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
-        write(fd, vs, strlen(vs));
-    }
-
-    if (!mHealthdConfig->batteryFullChargePath.empty()) {
-        snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
+        snprintf(vs, sizeof(vs), "  charge counter: %d\n", v);
         write(fd, vs, strlen(vs));
     }
 }
diff --git a/init/Android.bp b/init/Android.bp
index 016c05f..b209c47 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -292,9 +292,6 @@
         "make_f2fs",
         "mke2fs",
         "sload_f2fs",
-
-        // TODO: Revert after go/android-memcgv2-exp b/386797433
-        "memcgv2_activation_depth",
     ],
 }
 
@@ -602,6 +599,7 @@
     ],
     static_libs: [
         "libbase",
+        "libfstab",
         "libselinux",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
@@ -693,10 +691,3 @@
         default: ["init_first_stage"],
     }),
 }
-
-// TODO: Revert after go/android-memcgv2-exp b/386797433
-sh_binary {
-    name: "memcgv2_activation_depth",
-    src: "memcgv2_activation_depth.sh",
-    filename_from_src: true,
-}
diff --git a/init/README.md b/init/README.md
index c9742ad..6a66f14 100644
--- a/init/README.md
+++ b/init/README.md
@@ -971,26 +971,13 @@
 
 Bootcharting
 ------------
-This version of init contains code to perform "bootcharting": generating log
-files that can be later processed by the tools provided by <http://www.bootchart.org/>.
+Bootchart provides CPU and I/O load breakdown of all processes for the whole system.
+Refer to the instructions at
+ <https://source.android.com/docs/core/perf/boot-times#bootchart>.
 
 On the emulator, use the -bootchart _timeout_ option to boot with bootcharting
 activated for _timeout_ seconds.
 
-On a device:
-
-    adb shell 'touch /data/bootchart/enabled'
-
-Don't forget to delete this file when you're done collecting data!
-
-The log files are written to /data/bootchart/. A script is provided to
-retrieve them and create a bootchart.tgz file that can be used with the
-bootchart command-line utility:
-
-    sudo apt-get install pybootchartgui
-    # grab-bootchart.sh uses $ANDROID_SERIAL.
-    $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
-
 One thing to watch for is that the bootchart will show init as if it started
 running at 0s. You'll have to look at dmesg to work out when the kernel
 actually started init.
diff --git a/init/compare-bootcharts.py b/init/compare-bootcharts.py
index 009b639..b299b7d 100755
--- a/init/compare-bootcharts.py
+++ b/init/compare-bootcharts.py
@@ -47,7 +47,7 @@
 def analyze_process_maps(process_map1, process_map2, jiffy_record):
     # List interesting processes here
     processes_of_interest = [
-        '/init',
+        '/system/bin/init',
         '/system/bin/surfaceflinger',
         '/system/bin/bootanimation',
         'zygote64',
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 6bb0ad7..e06a645 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -402,7 +402,7 @@
 
     // /second_stage_resources is used to preserve files from first to second
     // stage init
-    CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOSUID | MS_NODEV,
+    CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"));
 
     if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
diff --git a/init/libprefetch/prefetch/prefetch.rc b/init/libprefetch/prefetch/prefetch.rc
index dee37bb..56fb827 100644
--- a/init/libprefetch/prefetch/prefetch.rc
+++ b/init/libprefetch/prefetch/prefetch.rc
@@ -1,28 +1,36 @@
-on init && property:ro.prefetch_boot.enabled=true
-    start prefetch
-
-service prefetch /system/bin/prefetch start
-    class main
-    user root
-    group root system
-    disabled
-    oneshot
-
-on property:prefetch_boot.record=true
-    start prefetch_record
+# Reads data from disk in advance and populates page cache
+# to speed up subsequent disk access.
+#
+# Record:
+#   start by `start prefetch_record` at appropriate timing.
+#   stop by setting `prefetch_boot.record_stop` to 1.
+#   set --duration to only capture for a certain duration instead.
+#
+# Replay:
+#   start by `start prefetch_replay` at appropriate timing.
+#   it will depend on several files generated from record.
+#
+#   replay is I/O intensive. make sure you pick appropriate
+#   timing to run each, so that you can maximize the page cache
+#   hit for subsequent disk access.
+#
+# Example:
+#   on early-init && property:ro.prefetch_boot.enabled=true
+#     start prefetch_replay
+#
+#   on init && property:ro.prefetch_boot.enabled=true
+#     start prefetch_record
+#
+#   on property:sys.boot_completed=1 && property:ro.prefetch_boot.enabled=true
+#     setprop prefetch_boot.record_stop 1
 
 service prefetch_record /system/bin/prefetch record --duration ${ro.prefetch_boot.duration_s:-0}
-    class main
     user root
     group root system
     disabled
     oneshot
 
-on property:prefetch_boot.replay=true
-    start prefetch_replay
-
-service prefetch_replay /system/bin/prefetch replay --io-depth ${ro.prefetch_boot.io_depth:-2} --max-fds ${ro.prefetch_boot.max_fds:-128}
-    class main
+service prefetch_replay /system/bin/prefetch replay --io-depth ${ro.prefetch_boot.io_depth:-2} --max-fds ${ro.prefetch_boot.max_fds:-1024}
     user root
     group root system
     disabled
diff --git a/init/libprefetch/prefetch/src/arch/android.rs b/init/libprefetch/prefetch/src/arch/android.rs
index a11767e..7d446ba 100644
--- a/init/libprefetch/prefetch/src/arch/android.rs
+++ b/init/libprefetch/prefetch/src/arch/android.rs
@@ -1,20 +1,23 @@
 use crate::Error;
 use crate::RecordArgs;
-use crate::StartArgs;
-use log::info;
 use log::warn;
 use std::fs::File;
 use std::fs::OpenOptions;
 use std::io::Write;
+use std::path::Path;
 use std::time::Duration;
 
 use rustutils::system_properties::error::PropertyWatcherError;
 use rustutils::system_properties::PropertyWatcher;
 
-const PREFETCH_RECORD_PROPERTY: &str = "prefetch_boot.record";
-const PREFETCH_REPLAY_PROPERTY: &str = "prefetch_boot.replay";
 const PREFETCH_RECORD_PROPERTY_STOP: &str = "prefetch_boot.record_stop";
 
+fn is_prefetch_enabled() -> Result<bool, Error> {
+    rustutils::system_properties::read_bool("ro.prefetch_boot.enabled", false).map_err(|e| {
+        Error::Custom { error: format!("Failed to read ro.prefetch_boot.enabled: {}", e) }
+    })
+}
+
 fn wait_for_property_true(
     property_name: &str,
     timeout: Option<Duration>,
@@ -31,68 +34,49 @@
     });
 }
 
-fn start_prefetch_service(property_name: &str) -> Result<(), Error> {
-    match rustutils::system_properties::write(property_name, "true") {
-        Ok(_) => {}
-        Err(_) => {
-            return Err(Error::Custom { error: "Failed to start prefetch service".to_string() });
-        }
-    }
-    Ok(())
-}
-
-/// Start prefetch service
-///
-/// 1: Check the presence of the file 'prefetch_ready'. If it doesn't
-/// exist then the device is booting for the first time after wipe.
-/// Thus, we would just create the file and exit as we do not want
-/// to initiate the record after data wipe primiarly because boot
-/// after data wipe is long and the I/O pattern during first boot may not actually match
-/// with subsequent boot.
-///
-/// 2: If the file 'prefetch_ready' is present:
-///
-///   a: Compare the build-finger-print of the device with the one record format
-///   is associated with by reading the file 'build_finger_print'. If they match,
-///   start the prefetch_replay.
-///
-///   b: If they don't match, then the device was updated through OTA. Hence, start
-///   a fresh record and delete the build-finger-print file. This should also cover
-///   the case of device rollback.
-///
-///   c: If the build-finger-print file doesn't exist, then just restart the record
-///   from scratch.
-pub fn start_prefetch(args: &StartArgs) -> Result<(), Error> {
-    if !args.path.exists() {
-        match File::create(args.path.clone()) {
-            Ok(_) => {}
-            Err(_) => {
-                return Err(Error::Custom { error: "File Creation failed".to_string() });
-            }
-        }
-        return Ok(());
+/// Checks if we can perform replay phase.
+/// Ensure that the pack file exists and is up-to-date, returns false otherwise.
+pub fn can_perform_replay(pack_path: &Path, fingerprint_path: &Path) -> Result<bool, Error> {
+    if !is_prefetch_enabled()? {
+        return Ok(false);
     }
 
-    if args.build_fingerprint_path.exists() {
-        let device_build_fingerprint = rustutils::system_properties::read("ro.build.fingerprint")
-            .map_err(|e| Error::Custom {
+    if !pack_path.exists() || !fingerprint_path.exists() {
+        return Ok(false);
+    }
+
+    let saved_fingerprint = std::fs::read_to_string(fingerprint_path)?;
+
+    let current_device_fingerprint = rustutils::system_properties::read("ro.build.fingerprint")
+        .map_err(|e| Error::Custom {
             error: format!("Failed to read ro.build.fingerprint: {}", e),
         })?;
-        let pack_build_fingerprint = std::fs::read_to_string(&args.build_fingerprint_path)?;
-        if pack_build_fingerprint.trim() == device_build_fingerprint.as_deref().unwrap_or_default()
-        {
-            info!("Start replay");
-            start_prefetch_service(PREFETCH_REPLAY_PROPERTY)?;
-        } else {
-            info!("Start record");
-            std::fs::remove_file(&args.build_fingerprint_path)?;
-            start_prefetch_service(PREFETCH_RECORD_PROPERTY)?;
-        }
-    } else {
-        info!("Start record");
-        start_prefetch_service(PREFETCH_RECORD_PROPERTY)?;
+
+    Ok(current_device_fingerprint.is_some_and(|fp| fp == saved_fingerprint.trim()))
+}
+
+/// Checks if we can perform record phase.
+/// Ensure that following conditions hold:
+///   - File specified in ready_path exists. otherwise, create a new file and return false.
+///   - can_perform_replay is false.
+pub fn ensure_record_is_ready(
+    ready_path: &Path,
+    pack_path: &Path,
+    fingerprint_path: &Path,
+) -> Result<bool, Error> {
+    if !is_prefetch_enabled()? {
+        return Ok(false);
     }
-    Ok(())
+
+    if !ready_path.exists() {
+        File::create(ready_path)
+            .map_err(|_| Error::Custom { error: "File Creation failed".to_string() })?;
+
+        return Ok(false);
+    }
+
+    let can_replay = can_perform_replay(pack_path, fingerprint_path)?;
+    Ok(!can_replay)
 }
 
 /// Write build finger print to associate prefetch pack file
diff --git a/init/libprefetch/prefetch/src/args.rs b/init/libprefetch/prefetch/src/args.rs
index e534210..4c1e689 100644
--- a/init/libprefetch/prefetch/src/args.rs
+++ b/init/libprefetch/prefetch/src/args.rs
@@ -25,8 +25,6 @@
 
 pub use args_internal::OutputFormat;
 pub use args_internal::ReplayArgs;
-#[cfg(target_os = "android")]
-pub use args_internal::StartArgs;
 pub use args_internal::TracerType;
 pub use args_internal::{DumpArgs, MainArgs, RecordArgs, SubCommands};
 use serde::Deserialize;
@@ -68,8 +66,6 @@
         SubCommands::Dump(arg) => {
             ensure_path_exists(&arg.path)?;
         }
-        #[cfg(target_os = "android")]
-        SubCommands::Start(_arg) => return Ok(()),
     }
     Ok(())
 }
diff --git a/init/libprefetch/prefetch/src/args/args_argh.rs b/init/libprefetch/prefetch/src/args/args_argh.rs
index 65084ee..d2251e6 100644
--- a/init/libprefetch/prefetch/src/args/args_argh.rs
+++ b/init/libprefetch/prefetch/src/args/args_argh.rs
@@ -40,12 +40,6 @@
     Replay(ReplayArgs),
     /// Dump prefetch data in human readable format
     Dump(DumpArgs),
-    /// Start prefetch service if possible
-    /// If the pack file is present, then prefetch replay is started
-    /// If the pack file is absent or if the build fingerprint
-    /// of the current pack file is different, then prefetch record is started.
-    #[cfg(target_os = "android")]
-    Start(StartArgs),
 }
 
 #[cfg(target_os = "android")]
@@ -58,22 +52,6 @@
     PathBuf::from("/metadata/prefetch/build_finger_print")
 }
 
-#[cfg(target_os = "android")]
-#[derive(Eq, PartialEq, Debug, Default, FromArgs)]
-/// Start prefetch service based on if pack file is present.
-#[argh(subcommand, name = "start")]
-pub struct StartArgs {
-    /// file path to check if prefetch_ready is present.
-    ///
-    /// A new file is created at the given path if it's not present.
-    #[argh(option, default = "default_ready_path()")]
-    pub path: PathBuf,
-
-    /// file path where build fingerprint is stored
-    #[argh(option, default = "default_build_finger_print_path()")]
-    pub build_fingerprint_path: PathBuf,
-}
-
 impl Default for SubCommands {
     fn default() -> Self {
         Self::Dump(DumpArgs::default())
@@ -147,6 +125,13 @@
     /// store build_finger_print to tie the pack format
     #[argh(option, default = "default_build_finger_print_path()")]
     pub build_fingerprint_path: PathBuf,
+
+    #[cfg(target_os = "android")]
+    /// file path to check if prefetch_ready is present.
+    ///
+    /// A new file is created at the given path if it's not present.
+    #[argh(option, default = "default_ready_path()")]
+    pub ready_path: PathBuf,
 }
 
 /// Type of tracing subsystem to use.
@@ -204,6 +189,11 @@
     /// file path from where the prefetch config file will be read
     #[argh(option, default = "PathBuf::new()")]
     pub config_path: PathBuf,
+
+    #[cfg(target_os = "android")]
+    /// store build_finger_print to tie the pack format
+    #[argh(option, default = "default_build_finger_print_path()")]
+    pub build_fingerprint_path: PathBuf,
 }
 
 /// dump records file in given format
diff --git a/init/libprefetch/prefetch/src/lib.rs b/init/libprefetch/prefetch/src/lib.rs
index 6564c4b..ea84c59 100644
--- a/init/libprefetch/prefetch/src/lib.rs
+++ b/init/libprefetch/prefetch/src/lib.rs
@@ -42,8 +42,6 @@
 pub use args::args_from_env;
 use args::OutputFormat;
 pub use args::ReplayArgs;
-#[cfg(target_os = "android")]
-pub use args::StartArgs;
 pub use args::{DumpArgs, MainArgs, RecordArgs, SubCommands};
 pub use error::Error;
 pub use format::FileId;
@@ -59,6 +57,13 @@
 
 /// Records prefetch data for the given configuration
 pub fn record(args: &RecordArgs) -> Result<(), Error> {
+    #[cfg(target_os = "android")]
+    if !ensure_record_is_ready(&args.ready_path, &args.path, &args.build_fingerprint_path)? {
+        info!("Cannot perform record -- skipping");
+        return Ok(());
+    }
+
+    info!("Starting record.");
     let (mut tracer, exit_tx) = tracer::Tracer::create(
         args.trace_buffer_size_kib,
         args.tracing_subsystem.clone(),
@@ -109,6 +114,13 @@
 
 /// Replays prefetch data for the given configuration
 pub fn replay(args: &ReplayArgs) -> Result<(), Error> {
+    #[cfg(target_os = "android")]
+    if !can_perform_replay(&args.path, &args.build_fingerprint_path)? {
+        info!("Cannot perform replay -- exiting.");
+        return Ok(());
+    }
+
+    info!("Starting replay.");
     let replay = Replay::new(args)?;
     replay.replay()
 }
diff --git a/init/libprefetch/prefetch/src/main.rs b/init/libprefetch/prefetch/src/main.rs
index eab826f..046e07e 100644
--- a/init/libprefetch/prefetch/src/main.rs
+++ b/init/libprefetch/prefetch/src/main.rs
@@ -22,8 +22,6 @@
 use prefetch_rs::init_logging;
 use prefetch_rs::record;
 use prefetch_rs::replay;
-#[cfg(target_os = "android")]
-use prefetch_rs::start_prefetch;
 use prefetch_rs::LogLevel;
 use prefetch_rs::MainArgs;
 use prefetch_rs::SubCommands;
@@ -35,8 +33,6 @@
         SubCommands::Record(args) => record(args),
         SubCommands::Replay(args) => replay(args),
         SubCommands::Dump(args) => dump(args),
-        #[cfg(target_os = "android")]
-        SubCommands::Start(args) => start_prefetch(args),
     };
 
     if let Err(err) = ret {
diff --git a/init/libprefetch/prefetch/src/tracer/mem.rs b/init/libprefetch/prefetch/src/tracer/mem.rs
index f69ae80..42120da 100644
--- a/init/libprefetch/prefetch/src/tracer/mem.rs
+++ b/init/libprefetch/prefetch/src/tracer/mem.rs
@@ -320,8 +320,8 @@
     // Convenience function to create regex. Used once per life of `record` but multiple times in
     // case of tests.
     pub fn get_trace_line_regex() -> Result<Regex, Error> {
-        // TODO: Fix this Regex expression for 5.15 kernels. This expression
-        // works only on 6.1+. Prior to 6.1, "<page>" was present in the output.
+        // `page=[hex]` entry exists in 5.x kernel format but not in 6.x.
+        // Conversely, `order=[digit]` entry exists in 6.x kernel format but not in 5.x.
         Regex::new(concat!(
             r"^\s+(?P<cmd_pid>\S+)",
             r"\s+(?P<cpu>\S+)",
@@ -330,9 +330,10 @@
             r"\s+mm_filemap_add_to_page_cache:",
             r"\s+dev\s+(?P<major>[0-9]+):(?P<minor>[0-9]+)",
             r"\s+ino\s+(?P<ino>\S+)",
-            //r"\s+(?P<page>\S+)",
+            r"(?:\s+(?P<page>page=\S+))?",
             r"\s+(?P<pfn>\S+)",
-            r"\s+ofs=(?P<offset>[0-9]+)"
+            r"\s+ofs=(?P<offset>[0-9]+)",
+            r"(?:\s+(?P<order>\S+))?"
         ))
         .map_err(|e| Error::Custom {
             error: format!("create regex for tracing failed with: {}", e),
@@ -682,22 +683,30 @@
 
     use super::*;
 
-    static TRACE_BUFFER: &str = r#"
- Settingide-502  [001] ....   484.360292: mm_filemap_add_to_page_CACHE: dev 254:6 ino cf1 page=68d477 pfn=59833 ofs=32768
- Settingide-502  [001] ....   484.360311: mm_filemap_add_to_page_cache: dev 254:6 ino cf1 page=759458 pfn=59827 ofs=57344
- BOX_ENTDED-3071 [001] ....   485.276715: mm_filemap_add_to_pag_ecache: dev 254:6 ino 1 page=00cc1c pfn=81748 ofs=13574144
- BOX_ENTDED-3071 [001] ....   485.276990: mm_filemap_add_to_page_cache: dev 254:6 ino cf2 page=36540b pfn=60952 ofs=0
- .gms.peent-843  [001] ....   485.545516: mm_filemap_add_to_page_cache: dev 254:6 ino 1 page=002e8b pfn=58928 ofs=13578240
- .gms.peent-843  [001] ....   485.545820: mm_filemap_add_to_page_cache: dev 254:6 ino cf3 page=6233ce pfn=58108 ofs=0
-      an.bg-459  [001] ....   494.029396: mm_filemap_add_to_page_cache: dev 254:3 ino 7cf page=c5b5c7 pfn=373933 ofs=1310720
-      an.bg-459  [001] ....   494.029398: mm_filemap_add_to_page_cache: dev 254:3 ino 7cf page=b8b9ec pfn=410074 ofs=1314816
-       "#;
+    static TRACE_BUFFER: &str = concat!(
+        // kernel 5.x
+        " Settingide-502  [001] ....   484.360292: mm_filemap_add_to_page_CACHE: dev 254:6 ino cf1 page=68d477 pfn=59833 ofs=32768\n",
+        " Settingide-502  [001] ....   484.360311: mm_filemap_add_to_page_cache: dev 254:6 ino cf1 page=759458 pfn=59827 ofs=57344\n",
+        " BOX_ENTDED-3071 [001] ....   485.276715: mm_filemap_add_to_pag_ecache: dev 254:6 ino 1 page=00cc1c pfn=81748 ofs=13574144\n",
+        " BOX_ENTDED-3071 [001] ....   485.276990: mm_filemap_add_to_page_cache: dev 254:6 ino cf2 page=36540b pfn=60952 ofs=0\n",
+        " .gms.peent-843  [001] ....   485.545516: mm_filemap_add_to_page_cache: dev 254:6 ino 1 page=002e8b pfn=58928 ofs=13578240\n",
+        " .gms.peent-843  [001] ....   485.545820: mm_filemap_add_to_page_cache: dev 254:6 ino cf3 page=6233ce pfn=58108 ofs=0\n",
+        "      an.bg-459  [001] ....   494.029396: mm_filemap_add_to_page_cache: dev 254:3 ino 7cf page=c5b5c7 pfn=373933 ofs=1310720\n",
+        "      an.bg-459  [001] ....   494.029398: mm_filemap_add_to_page_cache: dev 254:3 ino 7cf page=b8b9ec pfn=410074 ofs=1314816\n",
+
+        // kernel 6.x
+        " logcat-686     [006] ..... 148216.040320: mm_filemap_add_to_page_CACHE: dev 254:85 ino 3f15 pfn=0x213bc2 ofs=528384 order=0\n",
+        " logcat-686     [001] ..... 148217.776227: mm_filemap_add_to_page_cache: dev 254:85 ino 3f15 pfn=0x21d306 ofs=532480 order=0\n",
+        " logcat-686     [003] ..... 148219.044389: mm_filemap_add_to_pag_ecache: dev 254:85 ino 3f15 pfn=0x224b8d ofs=536576 order=0\n",
+        " logcat-686     [001] ..... 148220.780964: mm_filemap_add_to_page_cache: dev 254:85 ino 3f15 pfn=0x1bfe0a ofs=540672 order=0\n",
+        " logcat-686     [001] ..... 148223.046560: mm_filemap_add_to_page_cache: dev 254:85 ino 3f15 pfn=0x1f3d29 ofs=544768 order=0",
+    );
 
     fn sample_mem_traces() -> (String, Vec<Option<TraceLineInfo>>) {
         (
             TRACE_BUFFER.to_owned(),
             vec![
-                None,
+                // 5.x
                 None,
                 Some(TraceLineInfo::from_fields(254, 6, 0xcf1, 57344, 484360311000)),
                 None,
@@ -706,7 +715,12 @@
                 Some(TraceLineInfo::from_fields(254, 6, 0xcf3, 0, 485545820000)),
                 Some(TraceLineInfo::from_fields(254, 3, 0x7cf, 1310720, 494029396000)),
                 Some(TraceLineInfo::from_fields(254, 3, 0x7cf, 1314816, 494029398000)),
+                // 6.x
                 None,
+                Some(TraceLineInfo::from_fields(254, 85, 0x3f15, 532480, 148217776227000)),
+                None,
+                Some(TraceLineInfo::from_fields(254, 85, 0x3f15, 540672, 148220780964000)),
+                Some(TraceLineInfo::from_fields(254, 85, 0x3f15, 544768, 148223046560000)),
             ],
         )
     }
diff --git a/init/memcgv2_activation_depth.sh b/init/memcgv2_activation_depth.sh
deleted file mode 100644
index 91d215d..0000000
--- a/init/memcgv2_activation_depth.sh
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/bin/sh
-
-# This script adjusts overrides of the memcg v2 MaxActivationDepth value at runtime.
-# The override value needs to be accessible starting very early in the Android boot, where aconfig
-# flags and system properties do not work. A file on /metadata is used instead.
-
-# The kernel allows this to be as high as 65535, but our Android hierarchy is never that deep.
-MAX_ALLOWED_DEPTH=5
-
-# Store overridden MaxActivationDepths here for libprocessgroup to find them
-OVERRIDE_FILE_PATH="/metadata/libprocessgroup/memcg_v2_max_activation_depth"
-
-if [ "$#" -ne 1 ]
-then
-    echo "Usage: $0 <memcg v2 MaxActivationDepth value>"
-    exit 99
-fi
-
-max_activation_depth=$1
-
-if [[ $max_activation_depth != +([0-9]) ]]
-then
-    echo "MaxActivationDepth value must be a positive integer: $max_activation_depth"
-    exit 98
-fi
-
-if [ $max_activation_depth -lt 0 ]
-then
-    echo "Negative MaxActivationDepth is invalid: $max_activation_depth"
-    exit 97
-fi
-
-if [ $max_activation_depth -gt $MAX_ALLOWED_DEPTH ]
-then
-    echo "MaxActivationDepth is too large: $max_activation_depth"
-    exit 96
-fi
-
-grep memory /sys/fs/cgroup/cgroup.controllers
-if [ $? -ne 0 ]
-then
-    echo "memcg v2 is not available on this device!"
-    exit 95
-fi
-
-current_activation_depth=$(cat $OVERRIDE_FILE_PATH)
-if [ $? -ne 0 ]
-then
-    # Find the default activation depth in the absence of any properties / overrides.
-    #
-    # To do this 100% correctly requires JSON parsing which we don't really want to do here.
-    # We know that this will be called only for Pixel (for a limited-duration experiment), and that
-    # Pixel does not override cgroups.json, therefore we can assume that the system cgroups.json has
-    # only a single MaxActivationDepth entry which corresponds to the v2 memory controller. So we
-    # can just grep for the default value.
-    default_activation_depth=$(grep MaxActivationDepth /system/etc/cgroups.json | tr -dc '0-9')
-    if [ $? -ne 0 -o $default_activation_depth -gt $MAX_ALLOWED_DEPTH ]
-    then
-        # If MaxActivationDepth is not present, libprocessgroup does not limit how deep it will activate
-        default_activation_depth=$MAX_ALLOWED_DEPTH
-    fi
-    current_activation_depth=$default_activation_depth
-fi
-
-# libprocessgroup will pick this up for all future cgroup creations, including on the next boot
-echo $max_activation_depth > $OVERRIDE_FILE_PATH
-chmod ugo+r $OVERRIDE_FILE_PATH
-
-if [ $max_activation_depth -lt $current_activation_depth ]
-then
-    # We can deactivate memcgs which are deeper than the new depth value, however that would leave
-    # behind zombie memcgs which would ruin the metrics produced from this device. The only way to
-    # eliminate those zombies is to remove the entire cgroup, which we cannot do without killing
-    # all the contained processes. So the only real option we have is to reboot here, but that would
-    # look like a random reboot to users. So don't do anything now. Wait until the next reboot for
-    # the new setting to be applied.
-    :
-elif [ $max_activation_depth -gt $current_activation_depth ]
-then
-    for d in $(seq $max_activation_depth)
-    do
-        for f in $(find /sys/fs/cgroup/ -mindepth $d -maxdepth $d -name cgroup.subtree_control)
-        do
-            echo "+memory" > $f
-        done
-    done
-fi
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 2a27c1d..03fd2d2 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -56,6 +56,7 @@
 #include <linux/audit.h>
 #include <linux/netlink.h>
 #include <stdlib.h>
+#include <sys/mount.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
@@ -701,8 +702,8 @@
 }
 
 #ifdef ALLOW_REMOUNT_OVERLAYS
-void SetupOverlays() {
-    if (android::fs_mgr::use_override_creds) return;
+bool EarlySetupOverlays() {
+    if (android::fs_mgr::use_override_creds) return false;
 
     bool has_overlays = false;
     std::string contents;
@@ -715,8 +716,16 @@
             break;
         }
 
-    if (!has_overlays) return;
+    if (!has_overlays) return false;
+    if (mount("tmpfs", kSecondStageRes, "tmpfs", MS_REMOUNT | MS_NOSUID | MS_NODEV,
+              "mode=0755,uid=0,gid=0") == -1) {
+        PLOG(FATAL) << "Failed to remount tmpfs on " << kSecondStageRes << " to remove NO_EXEC";
+    }
 
+    return true;
+}
+
+void SetupOverlays() {
     // After adb remount, we mount all r/o volumes with overlayfs to allow writing.
     // However, since overlayfs performs its file operations in the context of the
     // mounting process, this will not work as is - init is in the kernel domain in
@@ -728,7 +737,6 @@
     // We will call overlay_remounter which will do the unmounts/mounts.
     // But for that to work, the volumes must not be busy, so we need to copy
     // overlay_remounter from system to a ramdisk and run it from there.
-
     const char* kOverlayRemounter = "overlay_remounter";
     auto or_src = std::filesystem::path("/system/xbin/") / kOverlayRemounter;
     auto or_dest = std::filesystem::path(kSecondStageRes) / kOverlayRemounter;
@@ -756,6 +764,9 @@
     PLOG(FATAL) << "execv(\"" << or_dest << "\") failed";
 }
 #else
+bool EarlySetupOverlays() {
+    return false;
+}
 void SetupOverlays() {}
 #endif
 
@@ -771,6 +782,9 @@
 
     SelinuxSetupKernelLogging();
 
+    // Test to see if we should use overlays, and if so remount tmpfs before selinux will block
+    bool use_overlays = EarlySetupOverlays();
+
     // TODO(b/287206497): refactor into different headers to only include what we need.
     if (IsMicrodroid()) {
         LoadSelinuxPolicyMicrodroid();
@@ -801,7 +815,7 @@
 
     // SetupOverlays does not return if overlays exist, instead it execs overlay_remounter
     // which then execs second stage init
-    SetupOverlays();
+    if (use_overlays) SetupOverlays();
 
     const char* path = "/system/bin/init";
     const char* args[] = {path, "second_stage", nullptr};
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 53b53ed..d75d4f1 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -27,6 +27,7 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <fstab/fstab.h>
 #include <selinux/selinux.h>
 #include <sys/signalfd.h>
 #include "lmkd_service.h"
@@ -280,5 +281,74 @@
 
 INSTANTIATE_TEST_SUITE_P(service, ServiceStopTest, testing::Values(false, true));
 
+// Entering a network namespace requires remounting sysfs to update contents of
+// /sys/class/net whose contents depend on the network namespace of the process
+// that mounted it rather than the effective network namespace of the reading
+// process.
+//
+// A side effect of the remounting is unmounting all filesystems mounted under
+// /sys, like tracefs. Verify that init doesn't leave them unmounted by
+// accident.
+TEST(service, enter_namespace_net_preserves_mounts) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+
+    struct ScopedNetNs {
+        std::string name;
+        ScopedNetNs(std::string n) : name(n) {
+            EXPECT_EQ(system(("/system/bin/ip netns add " + name).c_str()), 0);
+        }
+        ~ScopedNetNs() { EXPECT_EQ(system(("/system/bin/ip netns delete " + name).c_str()), 0); }
+    };
+    const ScopedNetNs netns("test_ns");
+
+    static constexpr std::string_view kServiceName = "ServiceA";
+    static constexpr std::string_view kScriptTemplate = R"init(
+service $name /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+    enter_namespace net /mnt/run/$ns_name
+)init";
+
+    std::string script = StringReplace(kScriptTemplate, "$name", kServiceName, false);
+    script = StringReplace(script, "$selabel", GetSecurityContext(), false);
+    script = StringReplace(script, "$ns_name", netns.name, false);
+
+    ServiceList& service_list = ServiceList::GetInstance();
+    Parser parser;
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
+
+    TemporaryFile tf;
+    ASSERT_GE(tf.fd, 0);
+    ASSERT_TRUE(WriteStringToFd(script, tf.fd));
+    ASSERT_TRUE(parser.ParseConfig(tf.path));
+
+    Service* const service = ServiceList::GetInstance().FindService(kServiceName);
+    ASSERT_NE(service, nullptr);
+    ASSERT_RESULT_OK(service->Start());
+    ASSERT_TRUE(service->IsRunning());
+
+    android::fs_mgr::Fstab root_mounts;
+    ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &root_mounts));
+
+    android::fs_mgr::Fstab ns_mounts;
+    ASSERT_TRUE(ReadFstabFromFile(StringReplace("/proc/$pid/mounts", "$pid",
+                                                std::to_string(service->pid()), /*all=*/false),
+                                  &ns_mounts));
+
+    for (const auto& expected_mount : root_mounts) {
+        auto it = std::find_if(ns_mounts.begin(), ns_mounts.end(), [&](const auto& ns_mount) {
+            return ns_mount.mount_point == expected_mount.mount_point;
+        });
+        EXPECT_TRUE(it != ns_mounts.end()) << StringPrintf(
+                "entering network namespace unmounted %s", expected_mount.mount_point.c_str());
+    }
+
+    ServiceList::GetInstance().RemoveService(*service);
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 0e19bcc..8d9a046 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -18,11 +18,11 @@
 
 #include <fcntl.h>
 #include <grp.h>
-#include <map>
 #include <sys/mount.h>
 #include <sys/prctl.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <map>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -31,6 +31,7 @@
 #include <android-base/strings.h>
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
+#include <fstab/fstab.h>
 #include <processgroup/processgroup.h>
 
 #include "mount_namespace.h"
@@ -82,12 +83,39 @@
         }
     }
     if (remount_sys) {
+        android::fs_mgr::Fstab mounts;
+        if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
+            LOG(ERROR) << "Could not read /proc/mounts";
+        }
         if (umount2("/sys", MNT_DETACH) == -1) {
             return ErrnoError() << "Could not umount(/sys)";
         }
-        if (mount("", "/sys", "sysfs", kSafeFlags, "") == -1) {
+        if (mount("sysfs", "/sys", "sysfs", kSafeFlags, "") == -1) {
             return ErrnoError() << "Could not mount(/sys)";
         }
+        // Unmounting /sys also unmounts all nested mounts like tracefs.
+        //
+        // Look up the filesystems that were mounted under /sys before we wiped
+        // it and attempt to restore them.
+        for (const auto& entry : mounts) {
+            // Never mount /sys/kernel/debug/tracing. This is the *one* mount
+            // that is special within Linux kernel: for backward compatibility
+            // tracefs gets auto-mounted there whenever one mounts debugfs [1].
+            //
+            // Attempting to mount the filesystem here will cause SELinux
+            // denials, because unlike *all other* filesystems in Android, it's
+            // not init who mounted it so there's no policy that would allow it.
+            //
+            // [1] https://lore.kernel.org/lkml/20150204143755.694479564@goodmis.org/
+            if (entry.mount_point.starts_with("/sys/") &&
+                entry.mount_point != "/sys/kernel/debug/tracing") {
+                if (mount(entry.blk_device.c_str(), entry.mount_point.c_str(),
+                          entry.fs_type.c_str(), entry.flags, "")) {
+                    LOG(WARNING) << "Could not mount(" << entry.mount_point
+                                 << ") after switching netns: " << ErrnoError().str();
+                }
+            }
+        }
     }
     return {};
 }
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 768db81..80c4f4c 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -44,6 +44,8 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
+#include "ashmem-internal.h"
+
 /* ashmem identity */
 static dev_t __ashmem_rdev;
 /*
@@ -76,8 +78,8 @@
  * debugging.
  */
 
-static bool debug_log = false;            /* set to true for verbose logging and other debug  */
-static bool pin_deprecation_warn = true; /* Log the pin deprecation warning only once */
+/* set to true for verbose logging and other debug  */
+static bool debug_log = false;
 
 /* Determine if vendor processes would be ok with memfd in the system:
  *
@@ -114,17 +116,8 @@
     // Check if kernel support exists, otherwise fall back to ashmem.
     // This code needs to build on old API levels, so we can't use the libc
     // wrapper.
-    //
-    // MFD_NOEXEC_SEAL is used to match the semantics of the ashmem device,
-    // which did not have executable permissions. This also seals the executable
-    // permissions of the buffer (i.e. they cannot be changed by fchmod()).
-    //
-    // MFD_NOEXEC_SEAL implies MFD_ALLOW_SEALING.
-    const char *test_buf_name = "test_android_memfd";
-    size_t buf_size = getpagesize();
-
     android::base::unique_fd fd(
-            syscall(__NR_memfd_create, test_buf_name, MFD_CLOEXEC | MFD_NOEXEC_SEAL));
+            syscall(__NR_memfd_create, "test_android_memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING));
     if (fd == -1) {
         ALOGE("memfd_create failed: %m, no memfd support");
         return false;
@@ -135,9 +128,9 @@
         return false;
     }
 
+    size_t buf_size = getpagesize();
     if (ftruncate(fd, buf_size) == -1) {
-        ALOGE("ftruncate(%s, %zd) failed to set memfd buffer size: %m, no memfd support",
-              test_buf_name, buf_size);
+        ALOGE("ftruncate(%zd) failed to set memfd buffer size: %m, no memfd support", buf_size);
         return false;
     }
 
@@ -158,12 +151,8 @@
     return true;
 }
 
-static bool has_memfd_support() {
-    /* memfd_supported is the initial global per-process state of what is known
-     * about memfd.
-     */
+bool has_memfd_support() {
     static bool memfd_supported = __has_memfd_support();
-
     return memfd_supported;
 }
 
@@ -173,75 +162,54 @@
     if (!android::base::ReadFileToString(boot_id_path, &boot_id)) {
         ALOGE("Failed to read %s: %m", boot_id_path.c_str());
         return "";
-    };
+    }
     boot_id = android::base::Trim(boot_id);
 
     return "/dev/ashmem" + boot_id;
 }
 
 /* logistics of getting file descriptor for ashmem */
-static int __ashmem_open_locked()
-{
+static int __ashmem_open_locked() {
     static const std::string ashmem_device_path = get_ashmem_device_path();
 
     if (ashmem_device_path.empty()) {
         return -1;
     }
 
-    int fd = TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC));
-
-    // fallback for APEX w/ use_vendor on Q, which would have still used /dev/ashmem
-    if (fd < 0) {
-        int saved_errno = errno;
-        fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
-        if (fd < 0) {
-            /* Q launching devices and newer must not reach here since they should have been
-             * able to open ashmem_device_path */
-            ALOGE("Unable to open ashmem device %s (error = %s) and /dev/ashmem(error = %s)",
-                  ashmem_device_path.c_str(), strerror(saved_errno), strerror(errno));
-            return fd;
-        }
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC)));
+    if (!fd.ok()) {
+        ALOGE("Unable to open ashmem device: %m");
+        return -1;
     }
+
     struct stat st;
-    int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
-    if (ret < 0) {
-        int save_errno = errno;
-        close(fd);
-        errno = save_errno;
-        return ret;
+    if (TEMP_FAILURE_RETRY(fstat(fd, &st)) == -1) {
+        return -1;
     }
     if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
-        close(fd);
         errno = ENOTTY;
         return -1;
     }
 
     __ashmem_rdev = st.st_rdev;
-    return fd;
+    return fd.release();
 }
 
-static int __ashmem_open()
-{
-    int fd;
-
+static int __ashmem_open() {
     pthread_mutex_lock(&__ashmem_lock);
-    fd = __ashmem_open_locked();
+    int fd = __ashmem_open_locked();
     pthread_mutex_unlock(&__ashmem_lock);
-
     return fd;
 }
 
 /* Make sure file descriptor references ashmem, negative number means false */
-static int __ashmem_is_ashmem(int fd, int fatal)
-{
-    dev_t rdev;
+static int __ashmem_is_ashmem(int fd, bool fatal) {
     struct stat st;
-
     if (fstat(fd, &st) < 0) {
         return -1;
     }
 
-    rdev = 0; /* Too much complexity to sniff __ashmem_rdev */
+    dev_t rdev = 0; /* Too much complexity to sniff __ashmem_rdev */
     if (S_ISCHR(st.st_mode) && st.st_rdev) {
         pthread_mutex_lock(&__ashmem_lock);
         rdev = __ashmem_rdev;
@@ -282,16 +250,15 @@
     return -1;
 }
 
-static int __ashmem_check_failure(int fd, int result)
-{
-    if (result == -1 && errno == ENOTTY) __ashmem_is_ashmem(fd, 1);
+static int __ashmem_check_failure(int fd, int result) {
+    if (result == -1 && errno == ENOTTY) __ashmem_is_ashmem(fd, true);
     return result;
 }
 
-static bool memfd_is_ashmem(int fd) {
+static bool is_ashmem_fd(int fd) {
     static bool fd_check_error_once = false;
 
-    if (__ashmem_is_ashmem(fd, 0) == 0) {
+    if (__ashmem_is_ashmem(fd, false) == 0) {
         if (!fd_check_error_once) {
             ALOGE("memfd: memfd expected but ashmem fd used - please use libcutils");
             fd_check_error_once = true;
@@ -303,25 +270,22 @@
     return false;
 }
 
-int ashmem_valid(int fd)
-{
-    if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+static bool is_memfd_fd(int fd) {
+    return has_memfd_support() && !is_ashmem_fd(fd);
+}
+
+int ashmem_valid(int fd) {
+    if (is_memfd_fd(fd)) {
         return 1;
     }
 
-    return __ashmem_is_ashmem(fd, 0) >= 0;
+    return __ashmem_is_ashmem(fd, false) >= 0;
 }
 
 static int memfd_create_region(const char* name, size_t size) {
     // This code needs to build on old API levels, so we can't use the libc
     // wrapper.
-    //
-    // MFD_NOEXEC_SEAL to match the semantics of the ashmem device, which did
-    // not have executable permissions. This also seals the executable
-    // permissions of the buffer (i.e. they cannot be changed by fchmod()).
-    //
-    // MFD_NOEXEC_SEAL implies MFD_ALLOW_SEALING.
-    android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_CLOEXEC | MFD_NOEXEC_SEAL));
+    android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_CLOEXEC | MFD_ALLOW_SEALING));
 
     if (fd == -1) {
         ALOGE("memfd_create(%s, %zd) failed: %m", name, size);
@@ -352,41 +316,20 @@
  * `name' is an optional label to give the region (visible in /proc/pid/maps)
  * `size' is the size of the region, in page-aligned bytes
  */
-int ashmem_create_region(const char *name, size_t size)
-{
-    int ret, save_errno;
+int ashmem_create_region(const char* name, size_t size) {
+    if (name == NULL) name = "none";
 
     if (has_memfd_support()) {
-        return memfd_create_region(name ? name : "none", size);
+        return memfd_create_region(name, size);
     }
 
-    int fd = __ashmem_open();
-    if (fd < 0) {
-        return fd;
+    android::base::unique_fd fd(__ashmem_open());
+    if (!fd.ok() ||
+        TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, name) < 0) ||
+        TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size) < 0)) {
+        return -1;
     }
-
-    if (name) {
-        char buf[ASHMEM_NAME_LEN] = {0};
-
-        strlcpy(buf, name, sizeof(buf));
-        ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
-        if (ret < 0) {
-            goto error;
-        }
-    }
-
-    ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
-    if (ret < 0) {
-        goto error;
-    }
-
-    return fd;
-
-error:
-    save_errno = errno;
-    close(fd);
-    errno = save_errno;
-    return ret;
+    return fd.release();
 }
 
 static int memfd_set_prot_region(int fd, int prot) {
@@ -418,61 +361,45 @@
     return 0;
 }
 
-int ashmem_set_prot_region(int fd, int prot)
-{
-    if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+int ashmem_set_prot_region(int fd, int prot) {
+    if (is_memfd_fd(fd)) {
         return memfd_set_prot_region(fd, prot);
     }
 
     return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot)));
 }
 
-int ashmem_pin_region(int fd, size_t offset, size_t len)
-{
-    if (!pin_deprecation_warn || debug_log) {
+static int do_pin(int op, int fd, size_t offset, size_t length) {
+    static bool already_warned_about_pin_deprecation = false;
+    if (!already_warned_about_pin_deprecation || debug_log) {
         ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.");
-        pin_deprecation_warn = true;
+        already_warned_about_pin_deprecation = true;
     }
 
-    if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+    if (is_memfd_fd(fd)) {
         return 0;
     }
 
     // TODO: should LP64 reject too-large offset/len?
-    ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
-    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin)));
+    ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(length) };
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, op, &pin)));
 }
 
-int ashmem_unpin_region(int fd, size_t offset, size_t len)
-{
-    if (!pin_deprecation_warn || debug_log) {
-        ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.");
-        pin_deprecation_warn = true;
-    }
-
-    if (has_memfd_support() && !memfd_is_ashmem(fd)) {
-        return 0;
-    }
-
-    // TODO: should LP64 reject too-large offset/len?
-    ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
-    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin)));
+int ashmem_pin_region(int fd, size_t offset, size_t length) {
+    return do_pin(ASHMEM_PIN, fd, offset, length);
 }
 
-int ashmem_get_size_region(int fd)
-{
-    if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+int ashmem_unpin_region(int fd, size_t offset, size_t length) {
+    return do_pin(ASHMEM_UNPIN, fd, offset, length);
+}
+
+int ashmem_get_size_region(int fd) {
+    if (is_memfd_fd(fd)) {
         struct stat sb;
-
         if (fstat(fd, &sb) == -1) {
             ALOGE("ashmem_get_size_region(%d): fstat failed: %m", fd);
             return -1;
         }
-
-        if (debug_log) {
-            ALOGD("ashmem_get_size_region(%d): %d", fd, static_cast<int>(sb.st_size));
-        }
-
         return sb.st_size;
     }
 
diff --git a/libcutils/ashmem-internal.h b/libcutils/ashmem-internal.h
new file mode 100644
index 0000000..7bd037b
--- /dev/null
+++ b/libcutils/ashmem-internal.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2025 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
+
+bool has_memfd_support();
diff --git a/libcutils/ashmem_test.cpp b/libcutils/ashmem_test.cpp
index 96f20db..2bf274c 100644
--- a/libcutils/ashmem_test.cpp
+++ b/libcutils/ashmem_test.cpp
@@ -29,6 +29,8 @@
 #include <cutils/ashmem.h>
 #include <gtest/gtest.h>
 
+#include "ashmem-internal.h"
+
 using android::base::unique_fd;
 
 static void TestCreateRegion(size_t size, unique_fd &fd, int prot) {
@@ -316,3 +318,119 @@
 
     ASSERT_NO_FATAL_FAILURE(ForkMultiRegionTest(fds, nRegions, size));
 }
+
+class AshmemTestMemfdAshmemCompat : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    if (!has_memfd_support()){
+        GTEST_SKIP() << "No memfd support; skipping memfd-ashmem compat tests";
+    }
+  }
+};
+
+TEST_F(AshmemTestMemfdAshmemCompat, SetNameTest) {
+    unique_fd fd;
+
+    // ioctl() should fail, since memfd names cannot be changed after the buffer has been created.
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |
+                                                                PROT_EXEC));
+    ASSERT_LT(ioctl(fd, ASHMEM_SET_NAME, "invalid-command"), 0);
+}
+
+TEST_F(AshmemTestMemfdAshmemCompat, GetNameTest) {
+    unique_fd fd;
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |
+                                                                PROT_EXEC));
+
+    char testBuf[ASHMEM_NAME_LEN];
+    ASSERT_EQ(0, ioctl(fd, ASHMEM_GET_NAME, &testBuf));
+    // ashmem_create_region(nullptr, ...) creates memfds with the name "none".
+    ASSERT_STREQ(testBuf, "none");
+}
+
+TEST_F(AshmemTestMemfdAshmemCompat, SetSizeTest) {
+    unique_fd fd;
+
+    // ioctl() should fail, since libcutils sets and seals the buffer size after creating it.
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |
+                                                                PROT_EXEC));
+    ASSERT_LT(ioctl(fd, ASHMEM_SET_SIZE, 2 * getpagesize()), 0);
+}
+
+TEST_F(AshmemTestMemfdAshmemCompat, GetSizeTest) {
+    unique_fd fd;
+    size_t bufSize = getpagesize();
+
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(bufSize, fd, PROT_READ | PROT_WRITE | PROT_EXEC));
+    ASSERT_EQ(static_cast<int>(bufSize), ioctl(fd, ASHMEM_GET_SIZE, 0));
+}
+
+TEST_F(AshmemTestMemfdAshmemCompat, ProtMaskTest) {
+    unique_fd fd;
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |
+                                                                PROT_EXEC));
+
+    // We can only change PROT_WRITE for memfds since memfd implements ashmem's prot_mask through
+    // file seals, and only write seals exist.
+    //
+    // All memfd files start off as being writable (i.e. PROT_WRITE is part of the prot_mask).
+    // Test to ensure that the implementation only clears the PROT_WRITE bit when requested.
+    ASSERT_EQ(0, ioctl(fd, ASHMEM_SET_PROT_MASK, PROT_READ | PROT_WRITE | PROT_EXEC));
+    int prot = ioctl(fd, ASHMEM_GET_PROT_MASK, 0);
+    ASSERT_NE(prot, -1);
+    ASSERT_TRUE(prot & PROT_WRITE) << prot;
+
+    ASSERT_EQ(0, ioctl(fd, ASHMEM_SET_PROT_MASK, PROT_READ | PROT_EXEC));
+    prot = ioctl(fd, ASHMEM_GET_PROT_MASK, 0);
+    ASSERT_NE(prot, -1);
+    ASSERT_TRUE(!(prot & PROT_WRITE)) << prot;
+
+    // The shim layer should implement clearing PROT_WRITE via file seals, so check the file
+    // seals to ensure that F_SEAL_FUTURE_WRITE is set.
+    int seals = fcntl(fd, F_GET_SEALS, 0);
+    ASSERT_NE(seals, -1);
+    ASSERT_TRUE(seals & F_SEAL_FUTURE_WRITE) << seals;
+
+    // Similarly, ensure that file seals affect prot_mask
+    unique_fd fd2;
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd2, PROT_READ | PROT_WRITE |
+                                                                PROT_EXEC));
+    ASSERT_EQ(0, fcntl(fd2, F_ADD_SEALS, F_SEAL_FUTURE_WRITE));
+    prot = ioctl(fd2, ASHMEM_GET_PROT_MASK, 0);
+    ASSERT_NE(prot, -1);
+    ASSERT_TRUE(!(prot & PROT_WRITE)) << prot;
+
+    // And finally, ensure that adding back permissions fails
+    ASSERT_LT(ioctl(fd2, ASHMEM_SET_PROT_MASK, PROT_READ | PROT_WRITE | PROT_EXEC), 0);
+}
+
+TEST_F(AshmemTestMemfdAshmemCompat, FileIDTest) {
+    unique_fd fd;
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |
+                                                                PROT_EXEC));
+
+    unsigned long ino;
+    ASSERT_EQ(0, ioctl(fd, ASHMEM_GET_FILE_ID, &ino));
+    struct stat st;
+    ASSERT_EQ(0, fstat(fd, &st));
+    ASSERT_EQ(ino, st.st_ino);
+}
+
+TEST_F(AshmemTestMemfdAshmemCompat, UnpinningTest) {
+    unique_fd fd;
+    size_t bufSize = getpagesize();
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |
+                                                                PROT_EXEC));
+
+    struct ashmem_pin pin = {
+        .offset = 0,
+        .len = static_cast<uint32_t>(bufSize),
+    };
+    ASSERT_EQ(0, ioctl(fd, ASHMEM_UNPIN, &pin));
+    // ASHMEM_UNPIN should just be a nop
+    ASSERT_EQ(ASHMEM_IS_PINNED, ioctl(fd, ASHMEM_GET_PIN_STATUS, 0));
+
+    // This shouldn't do anything; when we pin the page, it shouldn't have been purged.
+    ASSERT_EQ(0, ioctl(fd, ASHMEM_PURGE_ALL_CACHES, 0));
+    ASSERT_EQ(ASHMEM_NOT_PURGED, ioctl(fd, ASHMEM_PIN, &pin));
+}
\ No newline at end of file
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 00a1114..0d1b7fe 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -72,20 +72,14 @@
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data" },
     { 00755, AID_ROOT,         AID_SYSTEM,       0, "mnt" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "product/bin" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "product/apex/*/bin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
-    { 00750, AID_ROOT,         AID_SYSTEM,       0, "system/apex/com.android.tethering/bin/for-system" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/bin" },
     { 00755, AID_ROOT,         AID_ROOT,         0, "system/etc/ppp" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "system/vendor" },
     { 00750, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "system/apex/*/bin" },
-    { 00750, AID_ROOT,         AID_SYSTEM,       0, "system_ext/apex/com.android.tethering/bin/for-system" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/bin" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/apex/*/bin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "vendor/bin" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "vendor/apex/*/bin" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "vendor" },
     {},
         // clang-format on
@@ -182,8 +176,6 @@
 
     // the following files have enhanced capabilities and ARE included
     // in user builds.
-    { 06755, AID_CLAT,      AID_CLAT,      0, "system/apex/com.android.tethering/bin/for-system/clatd" },
-    { 06755, AID_CLAT,      AID_CLAT,      0, "system_ext/apex/com.android.tethering/bin/for-system/clatd" },
     { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
                                               "system/bin/inputflinger" },
     { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
@@ -214,23 +206,19 @@
     { 00644, AID_ROOT,      AID_ROOT,      0, "odm/app/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "odm/priv-app/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "product/bin/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "product/apex/*bin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "product/framework/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "product/app/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "product/priv-app/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/xbin/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "system/apex/*/bin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system/framework/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system/app/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system/priv-app/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system_ext/bin/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "system_ext/apex/*/bin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system_ext/framework/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system_ext/app/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system_ext/priv-app/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/apex/*bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "vendor/framework/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "vendor/app/*" },
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index dbf736a..e9345a5 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -36,7 +36,7 @@
         "Controller": "memory",
         "Path": ".",
         "NeedsActivation": true,
-        "MaxActivationDepth": 0,
+        "MaxActivationDepth": 3,
         "Optional": true
       }
     ]
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 720cb30..42cdb91 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -597,7 +597,7 @@
           "Params":
           {
             "Name": "MemSoftLimit",
-            "Value": "16MB"
+            "Value": "16M"
           }
         },
         {
@@ -619,7 +619,7 @@
           "Params":
           {
             "Name": "MemSoftLimit",
-            "Value": "512MB"
+            "Value": "512M"
           }
         },
         {
diff --git a/libprocessgroup/util/util.cpp b/libprocessgroup/util/util.cpp
index a15a44f..c772bc5 100644
--- a/libprocessgroup/util/util.cpp
+++ b/libprocessgroup/util/util.cpp
@@ -18,19 +18,15 @@
 
 #include <algorithm>
 #include <iterator>
-#include <mutex>
 #include <optional>
 #include <string_view>
 
 #include <mntent.h>
-#include <unistd.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
-#include <android-base/strings.h>
 #include <json/reader.h>
 #include <json/value.h>
 
@@ -178,38 +174,6 @@
     return mounts;
 }
 
-// Keep the override file open to reduce open syscalls, but read it every time.
-// Note that memcgv2_activation_depth.sh can race with us here.
-std::optional<unsigned int> ReadMaxActivationDepthMetadataOverride() {
-    static const char* OVERRIDE_FILE_PATH =
-        "/metadata/libprocessgroup/memcg_v2_max_activation_depth";
-    static int override_fd = open(OVERRIDE_FILE_PATH, O_RDONLY | O_CLOEXEC);
-    static std::mutex mtx;
-
-    std::unique_lock lock(mtx);
-    if (override_fd < 0) {
-        override_fd = open(OVERRIDE_FILE_PATH, O_RDONLY | O_CLOEXEC);
-        if (override_fd < 0) return std::nullopt;
-    }
-
-    std::string depth_str;
-    const bool ret = android::base::ReadFdToString(override_fd, &depth_str);
-    lseek(override_fd, 0, SEEK_SET);
-    lock.unlock();
-
-    if (!ret) {
-        PLOG(ERROR) << "Failed to read max activation depth override";
-        return std::nullopt;
-    }
-
-    unsigned int depth;
-    if (!android::base::ParseUint(android::base::Trim(depth_str), &depth)) {
-        PLOG(ERROR) << "Failed to convert max activation depth override (" << depth_str << ')';
-        return std::nullopt;
-    }
-    return depth;
-}
-
 }  // anonymous namespace
 
 
@@ -271,10 +235,7 @@
 bool ActivateControllers(const std::string& path, const CgroupDescriptorMap& descriptors) {
     for (const auto& [name, descriptor] : descriptors) {
         const uint32_t flags = descriptor.controller()->flags();
-        uint32_t max_activation_depth;
-        std::optional<unsigned int> metadataMaxDepth = ReadMaxActivationDepthMetadataOverride();
-        if (metadataMaxDepth) max_activation_depth = *metadataMaxDepth;
-        else max_activation_depth = descriptor.controller()->max_activation_depth();
+        const uint32_t max_activation_depth = descriptor.controller()->max_activation_depth();
         const unsigned int depth = GetCgroupDepth(descriptor.controller()->path(), path);
 
         if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) {
diff --git a/libsync/sync.c b/libsync/sync.c
index b8c48c7..c4c4472 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -117,7 +117,7 @@
     UAPI_MODERN,
     UAPI_LEGACY
 };
-static atomic_int g_uapi_version = ATOMIC_VAR_INIT(UAPI_UNKNOWN);
+static atomic_int g_uapi_version = UAPI_UNKNOWN;
 
 // ---------------------------------------------------------------------------
 
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index 3204a9f..c0d31d9 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -118,6 +118,12 @@
 }
 
 prebuilt_etc {
+    name: "init-mmd-prop.rc",
+    src: "init-mmd-prop.rc",
+    sub_dir: "init",
+}
+
+prebuilt_etc {
     name: "asan.options",
     src: "asan.options",
 }
diff --git a/rootdir/init-mmd-prop.rc b/rootdir/init-mmd-prop.rc
new file mode 100644
index 0000000..6e9191c
--- /dev/null
+++ b/rootdir/init-mmd-prop.rc
@@ -0,0 +1,19 @@
+on property:sys.boot_completed=1
+    # When mmd package is not included in the image, we need to initialize
+    # `mmd.enabled_aconfig` sysprop instead of `mmd --set-property`.
+    #
+    # This is because of the consideration for devices in Trunkfood and Nextfood
+    # under mmd being launched via AConfig flag. The devices set up zram with
+    # mmd if `mmd_enabled` AConfig flag is enabled, otherwise set up zram with
+    # swapon_all init command. Since AConfig does not support any init script
+    # integration, we use `mmd.enabled_aconfig` copied by `mmd --set-property`
+    # instead of AConfig flag itself and we need mmd.enabled_aconfig to be empty
+    # by default, to let swapon_all command wait until aconfig flag value is
+    # loaded to the system property.
+    # Devices in Trunkfood and Nextfood needs to execute swapon_all command on
+    # `on property:mmd.enabled_aconfig=*` trigger. So initializing
+    # `mmd.enabled_aconfig` sysprop is required on images without mmd package.
+    #
+    # Note that this init file must not be in the image if mmd is built into the
+    # image.
+    setprop mmd.enabled_aconfig false
\ No newline at end of file
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 404029b..df073d7 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -614,9 +614,6 @@
 
     mkdir /metadata/staged-install 0770 root system
 
-    # TODO: Revert after go/android-memcgv2-exp b/386797433
-    mkdir /metadata/libprocessgroup 0775 root system
-
 on late-fs
     # Ensure that tracefs has the correct permissions.
     # This does not work correctly if it is called in post-fs.
@@ -1234,7 +1231,7 @@
 # and chown/chmod does not work for /proc/sys/ entries.
 # So proxy writes through init.
 on property:sys.sysctl.extra_free_kbytes=*
-    exec -- /system/bin/extra_free_kbytes.sh ${sys.sysctl.extra_free_kbytes}
+    exec_background -- /system/bin/extra_free_kbytes.sh ${sys.sysctl.extra_free_kbytes}
 
 # Allow users to drop caches
 on property:perf.drop_caches=3
@@ -1321,34 +1318,14 @@
 # Multi-Gen LRU Experiment
 on property:persist.device_config.mglru_native.lru_gen_config=none
   write /sys/kernel/mm/lru_gen/enabled 0
-  # Memcg v2 Experiment
-  # TODO: Revert after go/android-memcgv2-exp b/386797433
-  exec - system system -- /system/bin/memcgv2_activation_depth.sh 0
-  setprop persist.device_config.lmkd_native.psi_partial_stall_ms 70
 on property:persist.device_config.mglru_native.lru_gen_config=core
-  write /sys/kernel/mm/lru_gen/enabled y
-  # Memcg v2 Experiment
-  # TODO: Revert after go/android-memcgv2-exp b/386797433
-  exec - system system -- /system/bin/memcgv2_activation_depth.sh 1
-  setprop persist.device_config.lmkd_native.psi_partial_stall_ms 56
+  write /sys/kernel/mm/lru_gen/enabled 1
 on property:persist.device_config.mglru_native.lru_gen_config=core_and_mm_walk
-  write /sys/kernel/mm/lru_gen/enabled y
-  # Memcg v2 Experiment
-  # TODO: Revert after go/android-memcgv2-exp b/386797433
-  exec - system system -- /system/bin/memcgv2_activation_depth.sh 1
-  setprop persist.device_config.lmkd_native.psi_partial_stall_ms 70
+  write /sys/kernel/mm/lru_gen/enabled 3
 on property:persist.device_config.mglru_native.lru_gen_config=core_and_nonleaf_young
-  write /sys/kernel/mm/lru_gen/enabled y
-  # Memcg v2 Experiment
-  # TODO: Revert after go/android-memcgv2-exp b/386797433
-  exec - system system -- /system/bin/memcgv2_activation_depth.sh 2
-  setprop persist.device_config.lmkd_native.psi_partial_stall_ms 70
+  write /sys/kernel/mm/lru_gen/enabled 5
 on property:persist.device_config.mglru_native.lru_gen_config=all
-  write /sys/kernel/mm/lru_gen/enabled y
-  # Memcg v2 Experiment
-  # TODO: Revert after go/android-memcgv2-exp b/386797433
-  exec - system system -- /system/bin/memcgv2_activation_depth.sh 3
-  setprop persist.device_config.lmkd_native.psi_partial_stall_ms 70
+  write /sys/kernel/mm/lru_gen/enabled 7
 
 # Allow other processes to run `snapshotctl` through `init`. This requires
 # `set_prop` permission on `snapshotctl_prop`.
diff --git a/storaged/main.cpp b/storaged/main.cpp
index bbed210..8e71180 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -25,13 +25,12 @@
 #include <sys/types.h>
 #include <vector>
 
-#include <android-base/macros.h>
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
 #include <binder/IPCThreadState.h>
-#include <cutils/android_get_control_file.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
 #include <cutils/sched_policy.h>
 #include <private/android_filesystem_config.h>
 
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index 7085743..6debb69 100644
--- a/storaged/storaged.rc
+++ b/storaged/storaged.rc
@@ -2,7 +2,6 @@
     class main
     capabilities DAC_READ_SEARCH
     priority 10
-    file /d/mmc0/mmc0:0001/ext_csd r
     task_profiles ServiceCapacityLow
     user root
     group package_info
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 9910aee..121837d 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -55,8 +55,6 @@
 "}"
 /* clang-format on */
 
-#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
-
 static const char *uuid_name = "com.android.ipc-unittest.srv.uuid";
 static const char *echo_name = "com.android.ipc-unittest.srv.echo";
 static const char *ta_only_name = "com.android.ipc-unittest.srv.ta_only";
@@ -906,14 +904,12 @@
 
 static int send_fd_test(const struct tipc_test_params* params) {
     int ret;
-    int dma_buf[] = {-1, -1, -1};
+    int dma_buf = -1;
     int fd = -1;
-    volatile char* buf[countof(dma_buf)] = {MAP_FAILED, MAP_FAILED, MAP_FAILED};
+    volatile char* buf = MAP_FAILED;
     BufferAllocator* allocator = NULL;
-    uint i;
 
     const size_t num_chunks = 10;
-    const size_t buf_size = memref_chunk_size * num_chunks;
 
     fd = tipc_connect(params->dev_name, receiver_name);
     if (fd < 0) {
@@ -929,86 +925,56 @@
         goto cleanup;
     }
 
-    for (i = 0; i < countof(dma_buf); i++) {
-        ret = DmabufHeapAlloc(allocator, "system", buf_size, 0, 0 /* legacy align */);
-        if (ret < 0) {
-            fprintf(stderr, "Failed to create dma-buf fd of size %zu err (%d)\n", buf_size, ret);
-            goto cleanup;
-        }
-        dma_buf[i] = ret;
+    size_t buf_size = memref_chunk_size * num_chunks;
+    dma_buf = DmabufHeapAlloc(allocator, "system", buf_size, 0, 0 /* legacy align */);
+    if (dma_buf < 0) {
+        ret = dma_buf;
+        fprintf(stderr, "Failed to create dma-buf fd of size %zu err (%d)\n", buf_size, ret);
+        goto cleanup;
     }
 
-    for (i = 0; i < countof(dma_buf); i++) {
-        buf[i] = mmap(0, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf[i], 0);
-        if (buf[i] == MAP_FAILED) {
-            fprintf(stderr, "Failed to map dma-buf: %s\n", strerror(errno));
-            ret = -1;
-            goto cleanup;
-        }
-
-        strcpy((char*)buf[i], "From NS");
+    buf = mmap(0, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
+    if (buf == MAP_FAILED) {
+        fprintf(stderr, "Failed to map dma-buf: %s\n", strerror(errno));
+        ret = -1;
+        goto cleanup;
     }
 
-    struct trusty_shm shm[] = {
-            {
-                    .fd = dma_buf[0],
-                    .transfer = TRUSTY_SHARE,
-            },
-            {
-                    .fd = dma_buf[0],
-                    .transfer = TRUSTY_SEND_SECURE_OR_SHARE,
-            },
-            {
-                    .fd = dma_buf[1],
-                    .transfer = TRUSTY_LEND,
-            },
-            {
-                    .fd = dma_buf[1],
-                    .transfer = TRUSTY_SEND_SECURE_OR_SHARE,
-            },
-            {
-                    .fd = dma_buf[2],
-                    .transfer = TRUSTY_SEND_SECURE_OR_SHARE,
-            },
+    strcpy((char*)buf, "From NS");
+
+    struct trusty_shm shm = {
+            .fd = dma_buf,
+            .transfer = TRUSTY_SHARE,
     };
 
-    for (i = 0; i < countof(shm); i++) {
-        ssize_t rc = tipc_send(fd, NULL, 0, &shm[i], 1);
-        if (rc < 0) {
-            fprintf(stderr, "tipc_send failed: %zd\n", rc);
-            ret = rc;
-            goto cleanup;
-        }
-        char c;
-        read(fd, &c, 1);
+    ssize_t rc = tipc_send(fd, NULL, 0, &shm, 1);
+    if (rc < 0) {
+        fprintf(stderr, "tipc_send failed: %zd\n", rc);
+        ret = rc;
+        goto cleanup;
     }
+    char c;
+    read(fd, &c, 1);
+    tipc_close(fd);
 
     ret = 0;
-    for (i = 0; i < countof(buf); i++) {
-        for (size_t skip = 0; skip < num_chunks; skip++) {
-            int cmp = strcmp("Hello from Trusty!", (const char*)&buf[i][skip * memref_chunk_size])
-                              ? (-1)
-                              : 0;
-            if (cmp) fprintf(stderr, "Failed: Unexpected content at page %zu in dmabuf\n", skip);
-            ret |= cmp;
-        }
+    for (size_t skip = 0; skip < num_chunks; skip++) {
+        int cmp = strcmp("Hello from Trusty!",
+                         (const char*)&buf[skip * memref_chunk_size]) ? (-1) : 0;
+        if (cmp)
+            fprintf(stderr, "Failed: Unexpected content at page %zu in dmabuf\n", skip);
+        ret |= cmp;
     }
 
 cleanup:
-    for (i = 0; i < countof(dma_buf); i++) {
-        if (buf[i] != MAP_FAILED) {
-            munmap((char*)buf[i], buf_size);
-        }
-        if (dma_buf[i] >= 0) {
-            close(dma_buf[i]);
-        }
+    if (buf != MAP_FAILED) {
+        munmap((char*)buf, buf_size);
     }
+    close(dma_buf);
     if (allocator) {
         FreeDmabufHeapBufferAllocator(allocator);
     }
-    if (fd >= 0) {
-        tipc_close(fd);
-    }
+    tipc_close(fd);
     return ret;
 }
 
diff --git a/trusty/storage/interface/Android.bp b/trusty/storage/interface/Android.bp
deleted file mode 100644
index 769f53d..0000000
--- a/trusty/storage/interface/Android.bp
+++ /dev/null
@@ -1,26 +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 {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_static {
-    name: "libtrustystorageinterface",
-    vendor_available: true,
-    system_ext_specific: true,
-    export_include_dirs: ["include"],
-}
diff --git a/trusty/storage/interface/include/trusty/interface/storage.h b/trusty/storage/interface/include/trusty/interface/storage.h
deleted file mode 100644
index 3291607..0000000
--- a/trusty/storage/interface/include/trusty/interface/storage.h
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2015-2016 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 <stdint.h>
-
-/*
- * Storage port names
- * @STORAGE_CLIENT_TD_PORT:     Port used by clients that require tamper and
- *                              rollback detection.
- * @STORAGE_CLIENT_TDEA_PORT:   Port used by clients that require storage before
- *                              the non-secure os has booted.
- * @STORAGE_CLIENT_TP_PORT:     Port used by clients that require tamper proof
- *                              storage. Note that non-secure code can prevent
-                                read and write operations from succeeding, but
-                                it cannot modify on-disk data.
- * @STORAGE_DISK_PROXY_PORT:    Port used by non-secure proxy server
- */
-#define STORAGE_CLIENT_TD_PORT     "com.android.trusty.storage.client.td"
-#define STORAGE_CLIENT_TDEA_PORT   "com.android.trusty.storage.client.tdea"
-#define STORAGE_CLIENT_TP_PORT     "com.android.trusty.storage.client.tp"
-#define STORAGE_DISK_PROXY_PORT    "com.android.trusty.storage.proxy"
-
-enum storage_cmd {
-	STORAGE_REQ_SHIFT = 1,
-	STORAGE_RESP_BIT  = 1,
-
-	STORAGE_RESP_MSG_ERR   = STORAGE_RESP_BIT,
-
-	STORAGE_FILE_DELETE    = 1 << STORAGE_REQ_SHIFT,
-	STORAGE_FILE_OPEN      = 2 << STORAGE_REQ_SHIFT,
-	STORAGE_FILE_CLOSE     = 3 << STORAGE_REQ_SHIFT,
-	STORAGE_FILE_READ      = 4 << STORAGE_REQ_SHIFT,
-	STORAGE_FILE_WRITE     = 5 << STORAGE_REQ_SHIFT,
-	STORAGE_FILE_GET_SIZE  = 6 << STORAGE_REQ_SHIFT,
-	STORAGE_FILE_SET_SIZE  = 7 << STORAGE_REQ_SHIFT,
-
-	STORAGE_RPMB_SEND      = 8 << STORAGE_REQ_SHIFT,
-
-	/* transaction support */
-	STORAGE_END_TRANSACTION = 9 << STORAGE_REQ_SHIFT,
-
-	STORAGE_FILE_GET_MAX_SIZE = 12 << STORAGE_REQ_SHIFT,
-};
-
-/**
- * enum storage_err - error codes for storage protocol
- * @STORAGE_NO_ERROR:           all OK
- * @STORAGE_ERR_GENERIC:        unknown error. Can occur when there's an internal server
- *                              error, e.g. the server runs out of memory or is in a bad state.
- * @STORAGE_ERR_NOT_VALID:      input not valid. May occur if the arguments passed
- *                              into the command are not valid, for example if the file handle
- *                              passed in is not a valid one.
- * @STORAGE_ERR_UNIMPLEMENTED:  the command passed in is not recognized
- * @STORAGE_ERR_ACCESS:         the file is not accessible in the requested mode
- * @STORAGE_ERR_NOT_FOUND:      the file was not found
- * @STORAGE_ERR_EXIST           the file exists when it shouldn't as in with OPEN_CREATE | OPEN_EXCLUSIVE.
- * @STORAGE_ERR_TRANSACT        returned by various operations to indicate that current transaction
- *                              is in error state. Such state could be only cleared by sending
- *                              STORAGE_END_TRANSACTION message.
- * @STORAGE_ERR_SYNC_FAILURE    indicates that the current operation failed to sync
- *                              to disk. Only returned if STORAGE_MSG_FLAG_PRE_COMMIT or
- *                              STORAGE_MSG_FLAG_POST_COMMIT was set for the request.
- */
-enum storage_err {
-	STORAGE_NO_ERROR          = 0,
-	STORAGE_ERR_GENERIC       = 1,
-	STORAGE_ERR_NOT_VALID     = 2,
-	STORAGE_ERR_UNIMPLEMENTED = 3,
-	STORAGE_ERR_ACCESS        = 4,
-	STORAGE_ERR_NOT_FOUND     = 5,
-	STORAGE_ERR_EXIST         = 6,
-	STORAGE_ERR_TRANSACT      = 7,
-	STORAGE_ERR_SYNC_FAILURE  = 8,
-};
-
-/**
- * storage_delete_flag - flags for controlling delete semantics
- */
-enum storage_file_delete_flag {
-	STORAGE_FILE_DELETE_MASK = 0,
-};
-
-/**
- * storage_file_flag - Flags to control 'open' semantics.
- * @STORAGE_FILE_OPEN_CREATE:           if this file does not exist, create it.
- * @STORAGE_FILE_OPEN_CREATE_EXCLUSIVE: causes STORAGE_FILE_OPEN_CREATE to fail if the file
- *                                      already exists. Only meaningful if used in combination
- *                                      with STORAGE_FILE_OPEN_CREATE.
- * @STORAGE_FILE_OPEN_TRUNCATE:         if this file already exists, discard existing content
- *                                      and open it as a new file. No change in semantics if the
- *                                      file does not exist.
- * @STORAGE_FILE_OPEN_MASK:             mask for all open flags supported in current protocol.
- *                                      All other bits must be set to 0.
- */
-enum storage_file_open_flag {
-	STORAGE_FILE_OPEN_CREATE             = (1 << 0),
-	STORAGE_FILE_OPEN_CREATE_EXCLUSIVE   = (1 << 1),
-	STORAGE_FILE_OPEN_TRUNCATE           = (1 << 2),
-	STORAGE_FILE_OPEN_MASK               = STORAGE_FILE_OPEN_CREATE |
-					       STORAGE_FILE_OPEN_TRUNCATE |
-					       STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
-};
-
-/**
- * enum storage_msg_flag - protocol-level flags in struct storage_msg
- * @STORAGE_MSG_FLAG_BATCH:                 if set, command belongs to a batch transaction.
- *                                          No response will be sent by the server until
- *                                          it receives a command with this flag unset, at
- *                                          which point a cumulative result for all messages
- *                                          sent with STORAGE_MSG_FLAG_BATCH will be sent.
- *                                          This is only supported by the non-secure disk proxy
- *                                          server.
- * @STORAGE_MSG_FLAG_PRE_COMMIT:            if set, indicates that server need to commit
- *                                          pending changes before processing this message.
- * @STORAGE_MSG_FLAG_POST_COMMIT:           if set, indicates that server need to commit
- *                                          pending changes after processing this message.
- * @STORAGE_MSG_FLAG_TRANSACT_COMPLETE:     if set, indicates that server need to commit
- *                                          current transaction after processing this message.
- *                                          It is an alias for STORAGE_MSG_FLAG_POST_COMMIT.
- * @STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT: if set, indicates that server needs to ensure
- *                                          that there is not a pending checkpoint for
- *                                          userdata before processing this message.
- */
-enum storage_msg_flag {
-    STORAGE_MSG_FLAG_BATCH = 0x1,
-    STORAGE_MSG_FLAG_PRE_COMMIT = 0x2,
-    STORAGE_MSG_FLAG_POST_COMMIT = 0x4,
-    STORAGE_MSG_FLAG_TRANSACT_COMPLETE = STORAGE_MSG_FLAG_POST_COMMIT,
-    STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT = 0x8,
-};
-
-/*
- * The following declarations are the message-specific contents of
- * the 'payload' element inside struct storage_msg.
- */
-
-/**
- * struct storage_file_delete_req - request format for STORAGE_FILE_DELETE
- * @flags: currently unused, must be set to 0.
- * @name:  the name of the file
- */
-struct storage_file_delete_req {
-	uint32_t flags;
-	char name[0];
-};
-
-/**
- * struct storage_file_open_req - request format for STORAGE_FILE_OPEN
- * @flags: any of enum storage_file_flag or'ed together
- * @name:  the name of the file
- */
-struct storage_file_open_req {
-	uint32_t flags;
-	char     name[0];
-};
-
-/**
- * struct storage_file_open_resp - response format for STORAGE_FILE_OPEN
- * @handle: opaque handle to the opened file. Only present on success.
- */
-struct storage_file_open_resp {
-	uint32_t handle;
-};
-
-/**
- * struct storage_file_close_req - request format for STORAGE_FILE_CLOSE
- * @handle: the handle for the file to close
- */
-struct storage_file_close_req {
-	uint32_t handle;
-};
-
-/**
- * struct storage_file_get_max_size_req - request format for
- *                                        STORAGE_FILE_GET_MAX_SIZE
- * @handle: the handle for the file whose max size is requested
- */
-struct storage_file_get_max_size_req {
-	uint32_t handle;
-};
-
-/**
- * struct storage_file_get_max_size_resp - response format for
- *                                         STORAGE_FILE_GET_MAX_SIZE
- * @max_size:   the maximum size of the file
- */
-struct storage_file_get_max_size_resp {
-	uint64_t max_size;
-};
-
-/**
- * struct storage_file_read_req - request format for STORAGE_FILE_READ
- * @handle: the handle for the file from which to read
- * @size:   the quantity of bytes to read from the file
- * @offset: the offset in the file from whence to read
- */
-struct storage_file_read_req {
-	uint32_t handle;
-	uint32_t size;
-	uint64_t offset;
-};
-
-/**
- * struct storage_file_read_resp - response format for STORAGE_FILE_READ
- * @data: beginning of data retrieved from file
- */
-struct storage_file_read_resp {
-	uint8_t data[0];
-};
-
-/**
- * struct storage_file_write_req - request format for STORAGE_FILE_WRITE
- * @handle:     the handle for the file to write to
- * @offset:     the offset in the file from whence to write
- * @__reserved: unused, must be set to 0.
- * @data:       beginning of the data to be written
- */
-struct storage_file_write_req {
-	uint64_t offset;
-	uint32_t handle;
-	uint32_t __reserved;
-	uint8_t  data[0];
-};
-
-/**
- * struct storage_file_get_size_req - request format for STORAGE_FILE_GET_SIZE
- * @handle: handle for which the size is requested
- */
-struct storage_file_get_size_req {
-	uint32_t handle;
-};
-
-/**
- * struct storage_file_get_size_resp - response format for STORAGE_FILE_GET_SIZE
- * @size:   the size of the file
- */
-struct storage_file_get_size_resp {
-	uint64_t size;
-};
-
-/**
- * struct storage_file_set_size_req - request format for STORAGE_FILE_SET_SIZE
- * @handle: the file handle
- * @size:   the desired size of the file
- */
-struct storage_file_set_size_req {
-	uint64_t size;
-	uint32_t handle;
-};
-
-/**
- * struct storage_rpmb_send_req - request format for STORAGE_RPMB_SEND
- * @reliable_write_size:        size in bytes of reliable write region
- * @write_size:                 size in bytes of write region
- * @read_size:                  number of bytes to read for a read request
- * @__reserved:                 unused, must be set to 0
- * @payload:                    start of reliable write region, followed by
- *                              write region.
- *
- * Only used in proxy<->server interface.
- */
-struct storage_rpmb_send_req {
-	uint32_t reliable_write_size;
-	uint32_t write_size;
-	uint32_t read_size;
-	uint32_t __reserved;
-	uint8_t  payload[0];
-};
-
-/**
- * struct storage_rpmb_send_resp: response type for STORAGE_RPMB_SEND
- * @data: the data frames frames retrieved from the MMC.
- */
-struct storage_rpmb_send_resp {
-	uint8_t data[0];
-};
-
-/**
- * struct storage_msg - generic req/resp format for all storage commands
- * @cmd:        one of enum storage_cmd
- * @op_id:      client chosen operation identifier for an instance
- *              of a command or atomic grouping of commands (transaction).
- * @flags:      one or many of enum storage_msg_flag or'ed together.
- * @size:       total size of the message including this header
- * @result:     one of enum storage_err
- * @__reserved: unused, must be set to 0.
- * @payload:    beginning of command specific message format
- */
-struct storage_msg {
-	uint32_t cmd;
-	uint32_t op_id;
-	uint32_t flags;
-	uint32_t size;
-	int32_t  result;
-	uint32_t __reserved;
-	uint8_t  payload[0];
-};
-
diff --git a/trusty/storage/lib/include/trusty/lib/storage.h b/trusty/storage/lib/include/trusty/lib/storage.h
index b8ddf67..4335619 100644
--- a/trusty/storage/lib/include/trusty/lib/storage.h
+++ b/trusty/storage/lib/include/trusty/lib/storage.h
@@ -16,8 +16,8 @@
 
 #pragma once
 
+#include <interface/storage/storage.h>
 #include <stdint.h>
-#include <trusty/interface/storage.h>
 
 #define STORAGE_MAX_NAME_LENGTH_BYTES 159
 
diff --git a/trusty/storage/proxy/ipc.h b/trusty/storage/proxy/ipc.h
index 2e366bb..020f121 100644
--- a/trusty/storage/proxy/ipc.h
+++ b/trusty/storage/proxy/ipc.h
@@ -15,8 +15,8 @@
  */
 #pragma once
 
+#include <interface/storage/storage.h>
 #include <stdint.h>
-#include <trusty/interface/storage.h>
 
 int ipc_connect(const char *device, const char *service_name);
 void ipc_disconnect(void);
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 04bdf9a..1761eec 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -15,8 +15,8 @@
  */
 #pragma once
 
+#include <interface/storage/storage.h>
 #include <stdint.h>
-#include <trusty/interface/storage.h>
 
 #include "watchdog.h"
 
diff --git a/trusty/storage/proxy/storage.h b/trusty/storage/proxy/storage.h
index 6dbfe37..f46f785 100644
--- a/trusty/storage/proxy/storage.h
+++ b/trusty/storage/proxy/storage.h
@@ -15,8 +15,8 @@
  */
 #pragma once
 
+#include <interface/storage/storage.h>
 #include <stdint.h>
-#include <trusty/interface/storage.h>
 
 /* Defined in watchdog.h */
 struct watcher;