Merge "init_kill_services_test: smoreland@ owners" into main
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index baddf65..08619b9 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -18,6 +18,7 @@
 #include <dlfcn.h>
 #include <err.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <linux/prctl.h>
 #include <malloc.h>
 #include <pthread.h>
@@ -69,7 +70,6 @@
 #include "crash_test.h"
 #include "debuggerd/handler.h"
 #include "gtest/gtest.h"
-#include "libdebuggerd/utility.h"
 #include "protocol.h"
 #include "tombstoned/tombstoned.h"
 #include "util.h"
@@ -86,6 +86,7 @@
 #define ARCH_SUFFIX ""
 #endif
 
+constexpr size_t kTagGranuleSize = 16;
 constexpr char kWaitForDebuggerKey[] = "debug.debuggerd.wait_for_debugger";
 
 #define TIMEOUT(seconds, expr)                                     \
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index fa67d46..2c72379 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -158,7 +158,7 @@
       }
     }
 
-    return std::move(result);
+    return result;
   }
 
   std::optional<CrashOutput> get_output(DebuggerdDumpType dump_type) {
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 32e8b88..192232d 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -34,6 +34,9 @@
   ],
   "kernel-presubmit": [
     {
+      "name": "adb-remount-sh"
+    },
+    {
       "name": "libdm_test"
     },
     {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 5156754..76578dd 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -28,6 +28,7 @@
 #include <sys/ioctl.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <sys/swap.h>
 #include <sys/types.h>
 #include <sys/utsname.h>
@@ -215,10 +216,6 @@
          */
         if (!(*fs_stat & FS_STAT_FULL_MOUNT_FAILED)) {  // already tried if full mount failed
             errno = 0;
-            if (fs_type == "ext4") {
-                // This option is only valid with ext4
-                tmpmnt_opts += ",nomblk_io_submit";
-            }
             ret = mount(blk_device.c_str(), target.c_str(), fs_type.c_str(), tmpmnt_flags,
                         tmpmnt_opts.c_str());
             PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type
@@ -930,7 +927,8 @@
 // attempted_idx: On return, will indicate which fstab entry
 //     succeeded. In case of failure, it will be the start_idx.
 // Sets errno to match the 1st mount failure on failure.
-static bool mount_with_alternatives(Fstab& fstab, int start_idx, int* end_idx, int* attempted_idx) {
+static bool mount_with_alternatives(Fstab& fstab, int start_idx, bool interrupted, int* end_idx,
+                                    int* attempted_idx) {
     unsigned long i;
     int mount_errno = 0;
     bool mounted = false;
@@ -949,6 +947,13 @@
             continue;
         }
 
+        if (interrupted) {
+            LINFO << __FUNCTION__ << "(): skipping fstab mountpoint=" << fstab[i].mount_point
+                  << " rec[" << i << "].fs_type=" << fstab[i].fs_type
+                  << " (previously interrupted during encryption step)";
+            continue;
+        }
+
         // fstab[start_idx].blk_device is already updated to /dev/dm-<N> by
         // AVB related functions. Copy it from start_idx to the current index i.
         if ((i != start_idx) && fstab[i].fs_mgr_flags.logical &&
@@ -1416,19 +1421,28 @@
     return GetEntryForMountPoint(&fstab, mount_point) != nullptr;
 }
 
+std::string fs_mgr_metadata_encryption_in_progress_file_name(const FstabEntry& entry) {
+    return entry.metadata_key_dir + "/in_progress";
+}
+
+bool WasMetadataEncryptionInterrupted(const FstabEntry& entry) {
+    if (!should_use_metadata_encryption(entry)) return false;
+    return access(fs_mgr_metadata_encryption_in_progress_file_name(entry).c_str(), R_OK) == 0;
+}
+
 // When multiple fstab records share the same mount_point, it will try to mount each
 // one in turn, and ignore any duplicates after a first successful mount.
 // Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
-MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
+int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
     int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
     int error_count = 0;
     CheckpointManager checkpoint_manager;
     AvbUniquePtr avb_handle(nullptr);
     bool wiped = false;
-
     bool userdata_mounted = false;
+
     if (fstab->empty()) {
-        return {FS_MGR_MNTALL_FAIL, userdata_mounted};
+        return FS_MGR_MNTALL_FAIL;
     }
 
     bool scratch_can_be_mounted = true;
@@ -1507,7 +1521,7 @@
                 if (!avb_handle) {
                     LERROR << "Failed to open AvbHandle";
                     set_type_property(encryptable);
-                    return {FS_MGR_MNTALL_FAIL, userdata_mounted};
+                    return FS_MGR_MNTALL_FAIL;
                 }
             }
             if (avb_handle->SetUpAvbHashtree(&current_entry, true /* wait_for_verity_dev */) ==
@@ -1530,7 +1544,9 @@
         int top_idx = i;
         int attempted_idx = -1;
 
-        bool mret = mount_with_alternatives(*fstab, i, &last_idx_inspected, &attempted_idx);
+        bool encryption_interrupted = WasMetadataEncryptionInterrupted(current_entry);
+        bool mret = mount_with_alternatives(*fstab, i, encryption_interrupted, &last_idx_inspected,
+                                            &attempted_idx);
         auto& attempted_entry = (*fstab)[attempted_idx];
         i = last_idx_inspected;
         int mount_errno = errno;
@@ -1541,7 +1557,7 @@
 
             if (status == FS_MGR_MNTALL_FAIL) {
                 // Fatal error - no point continuing.
-                return {status, userdata_mounted};
+                return status;
             }
 
             if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
@@ -1561,7 +1577,7 @@
                                   nullptr)) {
                         LERROR << "Encryption failed";
                         set_type_property(encryptable);
-                        return {FS_MGR_MNTALL_FAIL, userdata_mounted};
+                        return FS_MGR_MNTALL_FAIL;
                     }
                 }
             }
@@ -1579,13 +1595,18 @@
         // Mounting failed, understand why and retry.
         wiped = partition_wiped(current_entry.blk_device.c_str());
         if (mount_errno != EBUSY && mount_errno != EACCES &&
-            current_entry.fs_mgr_flags.formattable && wiped) {
+            current_entry.fs_mgr_flags.formattable && (wiped || encryption_interrupted)) {
             // current_entry and attempted_entry point at the same partition, but sometimes
             // at two different lines in the fstab.  Use current_entry for formatting
             // as that is the preferred one.
-            LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
-                   << " is wiped and " << current_entry.mount_point << " " << current_entry.fs_type
-                   << " is formattable. Format it.";
+            if (wiped)
+                LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
+                       << " is wiped and " << current_entry.mount_point << " "
+                       << current_entry.fs_type << " is formattable. Format it.";
+            if (encryption_interrupted)
+                LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
+                       << " was interrupted during encryption and " << current_entry.mount_point
+                       << " " << current_entry.fs_type << " is formattable. Format it.";
 
             checkpoint_manager.Revert(&current_entry);
 
@@ -1625,7 +1646,7 @@
         }
 
         // mount(2) returned an error, handle the encryptable/formattable case.
-        if (mount_errno != EBUSY && mount_errno != EACCES &&
+        if (mount_errno != EBUSY && mount_errno != EACCES && !encryption_interrupted &&
             should_use_metadata_encryption(attempted_entry)) {
             if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
                            attempted_entry.mount_point,
@@ -1643,13 +1664,13 @@
             // Use StringPrintf to output "(null)" instead.
             if (attempted_entry.fs_mgr_flags.no_fail) {
                 PERROR << android::base::StringPrintf(
-                        "Ignoring failure to mount an un-encryptable or wiped "
+                        "Ignoring failure to mount an un-encryptable, interrupted, or wiped "
                         "partition on %s at %s options: %s",
                         attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
                         attempted_entry.fs_options.c_str());
             } else {
                 PERROR << android::base::StringPrintf(
-                        "Failed to mount an un-encryptable or wiped partition "
+                        "Failed to mount an un-encryptable, interrupted, or wiped partition "
                         "on %s at %s options: %s",
                         attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
                         attempted_entry.fs_options.c_str());
@@ -1675,9 +1696,9 @@
     set_type_property(encryptable);
 
     if (error_count) {
-        return {FS_MGR_MNTALL_FAIL, userdata_mounted};
+        return FS_MGR_MNTALL_FAIL;
     } else {
-        return {encryptable, userdata_mounted};
+        return encryptable;
     }
 }
 
@@ -1714,190 +1735,6 @@
     return ret;
 }
 
-static std::chrono::milliseconds GetMillisProperty(const std::string& name,
-                                                   std::chrono::milliseconds default_value) {
-    auto value = GetUintProperty(name, static_cast<uint64_t>(default_value.count()));
-    return std::chrono::milliseconds(std::move(value));
-}
-
-static bool fs_mgr_unmount_all_data_mounts(const std::string& data_block_device) {
-    LINFO << __FUNCTION__ << "(): about to umount everything on top of " << data_block_device;
-    Timer t;
-    auto timeout = GetMillisProperty("init.userspace_reboot.userdata_remount.timeoutmillis", 5s);
-    while (true) {
-        bool umount_done = true;
-        Fstab proc_mounts;
-        if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
-            LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
-            return false;
-        }
-        // Now proceed with other bind mounts on top of /data.
-        for (const auto& entry : proc_mounts) {
-            std::string block_device;
-            if (StartsWith(entry.blk_device, "/dev/block") &&
-                !Realpath(entry.blk_device, &block_device)) {
-                PWARNING << __FUNCTION__ << "(): failed to realpath " << entry.blk_device;
-                block_device = entry.blk_device;
-            }
-            if (data_block_device == block_device) {
-                if (umount2(entry.mount_point.c_str(), 0) != 0) {
-                    PERROR << __FUNCTION__ << "(): Failed to umount " << entry.mount_point;
-                    umount_done = false;
-                }
-            }
-        }
-        if (umount_done) {
-            LINFO << __FUNCTION__ << "(): Unmounting /data took " << t;
-            return true;
-        }
-        if (t.duration() > timeout) {
-            LERROR << __FUNCTION__ << "(): Timed out unmounting all mounts on "
-                   << data_block_device;
-            Fstab remaining_mounts;
-            if (!ReadFstabFromFile("/proc/mounts", &remaining_mounts)) {
-                LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
-            } else {
-                LERROR << __FUNCTION__ << "(): Following mounts remaining";
-                for (const auto& e : remaining_mounts) {
-                    LERROR << __FUNCTION__ << "(): mount point: " << e.mount_point
-                           << " block device: " << e.blk_device;
-                }
-            }
-            return false;
-        }
-        std::this_thread::sleep_for(50ms);
-    }
-}
-
-static bool UnwindDmDeviceStack(const std::string& block_device,
-                                std::vector<std::string>* dm_stack) {
-    if (!StartsWith(block_device, "/dev/block/")) {
-        LWARNING << block_device << " is not a block device";
-        return false;
-    }
-    std::string current = block_device;
-    DeviceMapper& dm = DeviceMapper::Instance();
-    while (true) {
-        dm_stack->push_back(current);
-        if (!dm.IsDmBlockDevice(current)) {
-            break;
-        }
-        auto parent = dm.GetParentBlockDeviceByPath(current);
-        if (!parent) {
-            return false;
-        }
-        current = *parent;
-    }
-    return true;
-}
-
-FstabEntry* fs_mgr_get_mounted_entry_for_userdata(Fstab* fstab,
-                                                  const std::string& data_block_device) {
-    std::vector<std::string> dm_stack;
-    if (!UnwindDmDeviceStack(data_block_device, &dm_stack)) {
-        LERROR << "Failed to unwind dm-device stack for " << data_block_device;
-        return nullptr;
-    }
-    for (auto& entry : *fstab) {
-        if (entry.mount_point != "/data") {
-            continue;
-        }
-        std::string block_device;
-        if (entry.fs_mgr_flags.logical) {
-            if (!fs_mgr_update_logical_partition(&entry)) {
-                LERROR << "Failed to update logic partition " << entry.blk_device;
-                continue;
-            }
-            block_device = entry.blk_device;
-        } else if (!Realpath(entry.blk_device, &block_device)) {
-            PWARNING << "Failed to realpath " << entry.blk_device;
-            block_device = entry.blk_device;
-        }
-        if (std::find(dm_stack.begin(), dm_stack.end(), block_device) != dm_stack.end()) {
-            return &entry;
-        }
-    }
-    LERROR << "Didn't find entry that was used to mount /data onto " << data_block_device;
-    return nullptr;
-}
-
-// TODO(b/143970043): return different error codes based on which step failed.
-int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
-    Fstab proc_mounts;
-    if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
-        LERROR << "Can't read /proc/mounts";
-        return -1;
-    }
-    auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
-    if (mounted_entry == nullptr) {
-        LERROR << "/data is not mounted";
-        return -1;
-    }
-    std::string block_device;
-    if (!Realpath(mounted_entry->blk_device, &block_device)) {
-        PERROR << "Failed to realpath " << mounted_entry->blk_device;
-        return -1;
-    }
-    auto fstab_entry = fs_mgr_get_mounted_entry_for_userdata(fstab, block_device);
-    if (fstab_entry == nullptr) {
-        LERROR << "Can't find /data in fstab";
-        return -1;
-    }
-    bool force_umount = GetBoolProperty("sys.init.userdata_remount.force_umount", false);
-    if (force_umount) {
-        LINFO << "Will force an umount of userdata even if it's not required";
-    }
-    if (!force_umount && !SupportsCheckpoint(fstab_entry)) {
-        LINFO << "Userdata doesn't support checkpointing. Nothing to do";
-        return 0;
-    }
-    CheckpointManager checkpoint_manager;
-    if (!force_umount && !checkpoint_manager.NeedsCheckpoint()) {
-        LINFO << "Checkpointing not needed. Don't remount";
-        return 0;
-    }
-    if (!force_umount && fstab_entry->fs_mgr_flags.checkpoint_fs) {
-        // Userdata is f2fs, simply remount it.
-        if (!checkpoint_manager.Update(fstab_entry)) {
-            LERROR << "Failed to remount userdata in checkpointing mode";
-            return -1;
-        }
-        if (mount(block_device.c_str(), fstab_entry->mount_point.c_str(), "none",
-                  MS_REMOUNT | fstab_entry->flags, fstab_entry->fs_options.c_str()) != 0) {
-            PERROR << "Failed to remount userdata in checkpointing mode";
-            return -1;
-        }
-    } else {
-        LINFO << "Unmounting /data before remounting into checkpointing mode";
-        if (!fs_mgr_unmount_all_data_mounts(block_device)) {
-            LERROR << "Failed to umount /data";
-            return -1;
-        }
-        DeviceMapper& dm = DeviceMapper::Instance();
-        while (dm.IsDmBlockDevice(block_device)) {
-            auto next_device = dm.GetParentBlockDeviceByPath(block_device);
-            auto name = dm.GetDmDeviceNameByPath(block_device);
-            if (!name) {
-                LERROR << "Failed to get dm-name for " << block_device;
-                return -1;
-            }
-            LINFO << "Deleting " << block_device << " named " << *name;
-            if (!dm.DeleteDevice(*name, 3s)) {
-                return -1;
-            }
-            if (!next_device) {
-                LERROR << "Failed to find parent device for " << block_device;
-            }
-            block_device = *next_device;
-        }
-        LINFO << "Remounting /data";
-        // TODO(b/143970043): remove this hack after fs_mgr_mount_all is refactored.
-        auto result = fs_mgr_mount_all(fstab, MOUNT_MODE_ONLY_USERDATA);
-        return result.code == FS_MGR_MNTALL_FAIL ? -1 : 0;
-    }
-    return 0;
-}
-
 // wrapper to __mount() and expects a fully prepared fstab_rec,
 // unlike fs_mgr_do_mount which does more things with avb / verity etc.
 int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_point) {
@@ -2069,11 +1906,45 @@
     return true;
 }
 
+/*
+ * Zram backing device can be created as long as /data has at least `size`
+ * free space, though we may want to leave some extra space for the remaining
+ * boot process and other system activities.
+ */
+static bool ZramBackingDeviceSizeAvailable(off64_t size) {
+    constexpr const char* data_path = "/data";
+    uint64_t min_free_mb =
+            android::base::GetUintProperty<uint64_t>("ro.zram_backing_device_min_free_mb", 0);
+
+    // No min_free property. Skip the available size check.
+    if (min_free_mb == 0) return true;
+
+    struct statvfs vst;
+    if (statvfs(data_path, &vst) < 0) {
+        PERROR << "Cannot check available space: " << data_path;
+        return false;
+    }
+
+    uint64_t size_free = static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize;
+    uint64_t size_required = size + (min_free_mb * 1024 * 1024);
+    if (size_required > size_free) {
+        PERROR << "Free space is not enough for zram backing device: " << size_required << " > "
+               << size_free;
+        return false;
+    }
+    return true;
+}
+
 static bool PrepareZramBackingDevice(off64_t size) {
 
     constexpr const char* file_path = "/data/per_boot/zram_swap";
     if (size == 0) return true;
 
+    // Check available space
+    if (!ZramBackingDeviceSizeAvailable(size)) {
+        PERROR << "No space for target path: " << file_path;
+        return false;
+    }
     // Prepare target path
     unique_fd target_fd(TEMP_FAILURE_RETRY(open(file_path, O_RDWR | O_CREAT | O_CLOEXEC, 0600)));
     if (target_fd.get() == -1) {
@@ -2082,6 +1953,7 @@
     }
     if (fallocate(target_fd.get(), 0, 0, size) < 0) {
         PERROR << "Cannot truncate target path: " << file_path;
+        unlink(file_path);
         return false;
     }
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index bc4a7a6..af5ae2d 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -58,13 +58,8 @@
 #define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
 #define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
 #define FS_MGR_MNTALL_FAIL (-1)
-
-struct MountAllResult {
-    // One of the FS_MGR_MNTALL_* returned code defined above.
-    int code;
-    // Whether userdata was mounted as a result of |fs_mgr_mount_all| call.
-    bool userdata_mounted;
-};
+// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
+int fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
 
 struct HashtreeInfo {
     // The hash algorithm used to build the merkle tree.
@@ -75,13 +70,6 @@
     bool check_at_most_once;
 };
 
-// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
-// Returns a |MountAllResult|. The first element is one of the FS_MNG_MNTALL_* return codes
-// defined above, and the second element tells whether this call to fs_mgr_mount_all was responsible
-// for mounting userdata. Later is required for init to correctly enqueue fs-related events as part
-// of userdata remount during userspace reboot.
-MountAllResult fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
-
 #define FS_MGR_DOMNT_FAILED (-1)
 #define FS_MGR_DOMNT_BUSY (-2)
 #define FS_MGR_DOMNT_SUCCESS 0
@@ -127,11 +115,6 @@
 // it destroys verity devices from device mapper after the device is unmounted.
 int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
 
-// Finds a entry in |fstab| that was used to mount a /data on |data_block_device|.
-android::fs_mgr::FstabEntry* fs_mgr_get_mounted_entry_for_userdata(
-        android::fs_mgr::Fstab* fstab, const std::string& data_block_device);
-int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab);
-
 // Finds the dm_bow device on which this block device is stacked, or returns
 // empty string
 std::string fs_mgr_find_bow_device(const std::string& block_device);
@@ -144,3 +127,7 @@
 // Unlike fs_mgr_overlayfs, mount overlayfs without upperdir and workdir, so the
 // filesystem cannot be remount read-write.
 bool fs_mgr_mount_overlayfs_fstab_entry(const android::fs_mgr::FstabEntry& entry);
+
+// File name used to track if encryption was interrupted, leading to a known bad fs state
+std::string fs_mgr_metadata_encryption_in_progress_file_name(
+        const android::fs_mgr::FstabEntry& entry);
diff --git a/fs_mgr/libfstab/fstab.cpp b/fs_mgr/libfstab/fstab.cpp
index f00e0dc..21d2e2e 100644
--- a/fs_mgr/libfstab/fstab.cpp
+++ b/fs_mgr/libfstab/fstab.cpp
@@ -520,6 +520,24 @@
 
 }  // namespace
 
+// Return the path to the recovery fstab file.  There may be multiple fstab files;
+// the one that is returned will be the first that exists of recovery.fstab.<fstab_suffix>,
+// recovery.fstab.<hardware>, and recovery.fstab.<hardware.platform>.
+std::string GetRecoveryFstabPath() {
+    for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
+        std::string suffix;
+
+        if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
+
+        std::string fstab_path = "/etc/recovery.fstab." + suffix;
+        if (access(fstab_path.c_str(), F_OK) == 0) {
+            return fstab_path;
+        }
+    }
+
+    return "/etc/recovery.fstab";
+}
+
 // Return the path to the fstab file.  There may be multiple fstab files; the
 // one that is returned will be the first that exists of fstab.<fstab_suffix>,
 // fstab.<hardware>, and fstab.<hardware.platform>.  The fstab is searched for
@@ -529,7 +547,7 @@
 // the system/etc directory is supported too and is the preferred location.
 std::string GetFstabPath() {
     if (InRecovery()) {
-        return "/etc/recovery.fstab";
+        return GetRecoveryFstabPath();
     }
     for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
         std::string suffix;
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 2e948dd..62f9901 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -229,6 +229,10 @@
 
     // Enable direct reads from source device
     bool o_direct = 12;
+
+    // Number of cow operations to be merged at once
+    uint32 cow_op_merge_size = 13;
+
 }
 
 // Next: 10
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index 0ab6103..e0969f4 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -104,6 +104,24 @@
     return first_stage_init_;
 }
 
+bool DeviceInfo::SetActiveBootSlot([[maybe_unused]] unsigned int slot) {
+#ifdef LIBSNAPSHOT_USE_HAL
+    if (!EnsureBootHal()) {
+        return false;
+    }
+
+    CommandResult result = boot_control_->SetActiveBootSlot(slot);
+    if (!result.success) {
+        LOG(ERROR) << "Error setting slot " << slot << " active: " << result.errMsg;
+        return false;
+    }
+    return true;
+#else
+    LOG(ERROR) << "HAL support not enabled.";
+    return false;
+#endif
+}
+
 bool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) {
 #ifdef LIBSNAPSHOT_USE_HAL
     if (!EnsureBootHal()) {
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index d06f1be..9153abb 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -36,6 +36,7 @@
     std::string GetSuperDevice(uint32_t slot) const override;
     bool IsOverlayfsSetup() const override;
     bool SetBootControlMergeStatus(MergeStatus status) override;
+    bool SetActiveBootSlot(unsigned int slot) override;
     bool SetSlotAsUnbootable(unsigned int slot) override;
     bool IsRecovery() const override;
     std::unique_ptr<IImageManager> OpenImageManager() const override;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
index 573a85b..ca1ac1e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
@@ -29,6 +29,7 @@
     MOCK_METHOD(const android::fs_mgr::IPartitionOpener&, GetPartitionOpener, (), (const));
     MOCK_METHOD(bool, IsOverlayfsSetup, (), (const, override));
     MOCK_METHOD(bool, SetBootControlMergeStatus, (MergeStatus status), (override));
+    MOCK_METHOD(bool, SetActiveBootSlot, (unsigned int slot), (override));
     MOCK_METHOD(bool, SetSlotAsUnbootable, (unsigned int slot), (override));
     MOCK_METHOD(bool, IsRecovery, (), (const, override));
     MOCK_METHOD(bool, IsFirstStageInit, (), (const, override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 1ec8634..deb2d6e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -104,6 +104,7 @@
         virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;
         virtual bool IsOverlayfsSetup() const = 0;
         virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
+        virtual bool SetActiveBootSlot(unsigned int slot) = 0;
         virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
         virtual bool IsRecovery() const = 0;
         virtual bool IsTestDevice() const { return false; }
@@ -675,6 +676,8 @@
     std::string GetBootSnapshotsWithoutSlotSwitchPath();
     std::string GetSnapuserdFromSystemPath();
 
+    bool HasForwardMergeIndicator();
+
     const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
 
     bool MapAllPartitions(LockedFile* lock, const std::string& super_device, uint32_t slot,
@@ -785,11 +788,8 @@
     bool UpdateForwardMergeIndicator(bool wipe);
 
     // Helper for HandleImminentDataWipe.
-    // Call ProcessUpdateState and handle states with special rules before data wipe. Specifically,
-    // if |allow_forward_merge| and allow-forward-merge indicator exists, initiate merge if
-    // necessary.
-    UpdateState ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
-                                             const std::function<bool()>& callback);
+    // Call ProcessUpdateState and handle states with special rules before data wipe.
+    UpdateState ProcessUpdateStateOnDataWipe(const std::function<bool()>& callback);
 
     // Return device string of a mapped image, or if it is not available, the mapped image path.
     bool GetMappedImageDeviceStringOrPath(const std::string& device_name,
@@ -831,6 +831,8 @@
     // Check if direct reads are enabled for the source image
     bool UpdateUsesODirect(LockedFile* lock);
 
+    // Get value of maximum cow op merge size
+    uint32_t GetUpdateCowOpMergeSize(LockedFile* lock);
     // Wrapper around libdm, with diagnostics.
     bool DeleteDeviceIfExists(const std::string& name,
                               const std::chrono::milliseconds& timeout_ms = {});
@@ -846,7 +848,6 @@
     std::string metadata_dir_;
     std::unique_ptr<IImageManager> images_;
     bool use_first_stage_snapuserd_ = false;
-    bool in_factory_data_reset_ = false;
     std::function<bool(const std::string&)> uevent_regen_callback_;
     std::unique_ptr<SnapuserdClient> snapuserd_client_;
     std::unique_ptr<LpMetadata> old_partition_metadata_;
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 90813fe..620b03c 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -30,6 +30,8 @@
 #include <storage_literals/storage_literals.h>
 #include <update_engine/update_metadata.pb.h>
 
+#include "utility.h"
+
 namespace android {
 namespace snapshot {
 
@@ -90,6 +92,7 @@
     }
     bool IsOverlayfsSetup() const override { return false; }
     bool IsRecovery() const override { return recovery_; }
+    bool SetActiveBootSlot([[maybe_unused]] unsigned int slot) override { return true; }
     bool SetSlotAsUnbootable(unsigned int slot) override {
         unbootable_slots_.insert(slot);
         return true;
@@ -234,5 +237,21 @@
 
 #define RETURN_IF_NON_VIRTUAL_AB() RETURN_IF_NON_VIRTUAL_AB_MSG("")
 
+#define SKIP_IF_VENDOR_ON_ANDROID_S()                                        \
+    do {                                                                     \
+        if (IsVendorFromAndroid12())                                         \
+            GTEST_SKIP() << "Skip test as Vendor partition is on Android S"; \
+    } while (0)
+
+#define RETURN_IF_VENDOR_ON_ANDROID_S_MSG(msg) \
+    do {                                       \
+        if (IsVendorFromAndroid12()) {         \
+            std::cerr << (msg);                \
+            return;                            \
+        }                                      \
+    } while (0)
+
+#define RETURN_IF_VENDOR_ON_ANDROID_S() RETURN_IF_VENDOR_ON_ANDROID_S_MSG("")
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index a4a2c1a..8356c0c 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -250,8 +250,8 @@
                                 .target_partition = system_b,
                                 .current_metadata = builder_a.get(),
                                 .current_suffix = "_a",
-                                .using_snapuserd = true,
-                                .update = &update};
+                                .update = &update,
+                                .using_snapuserd = true};
 
     auto ret = creator.Run();
     ASSERT_TRUE(ret.has_value());
@@ -276,8 +276,8 @@
                                 .target_partition = system_b,
                                 .current_metadata = builder_a.get(),
                                 .current_suffix = "_a",
-                                .using_snapuserd = true,
-                                .update = nullptr};
+                                .update = nullptr,
+                                .using_snapuserd = true};
 
     auto ret = creator.Run();
     ASSERT_FALSE(ret.has_value());
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 4d55315..108fd90 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -20,7 +20,9 @@
 #include <sys/file.h>
 #include <sys/types.h>
 #include <sys/unistd.h>
+#include <sys/xattr.h>
 
+#include <chrono>
 #include <filesystem>
 #include <optional>
 #include <thread>
@@ -90,6 +92,8 @@
 static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
 static constexpr char kSnapuserdFromSystem[] = "/metadata/ota/snapuserd-from-system";
 static constexpr auto kUpdateStateCheckInterval = 2s;
+static constexpr char kOtaFileContext[] = "u:object_r:ota_metadata_file:s0";
+
 /*
  * The readahead size is set to 32kb so that
  * there is no significant memory pressure (/proc/pressure/memory) during boot.
@@ -1705,6 +1709,10 @@
         if (UpdateUsesODirect(lock.get())) {
             snapuserd_argv->emplace_back("-o_direct");
         }
+        uint cow_op_merge_size = GetUpdateCowOpMergeSize(lock.get());
+        if (cow_op_merge_size != 0) {
+            snapuserd_argv->emplace_back("-cow_op_merge_size=" + std::to_string(cow_op_merge_size));
+        }
     }
 
     size_t num_cows = 0;
@@ -2127,6 +2135,11 @@
     return update_status.o_direct();
 }
 
+uint32_t SnapshotManager::GetUpdateCowOpMergeSize(LockedFile* lock) {
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    return update_status.cow_op_merge_size();
+}
+
 bool SnapshotManager::MarkSnapuserdFromSystem() {
     auto path = GetSnapuserdFromSystemPath();
 
@@ -2134,6 +2147,24 @@
         PLOG(ERROR) << "Unable to write to vendor update path: " << path;
         return false;
     }
+
+    unique_fd fd(open(path.c_str(), O_PATH));
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to open file: " << path;
+        return false;
+    }
+
+    /*
+     * This function is invoked by first stage init and hence we need to
+     * explicitly set the correct selinux label for this file as update_engine
+     * will try to remove this file later on once the snapshot merge is
+     * complete.
+     */
+    if (fsetxattr(fd.get(), XATTR_NAME_SELINUX, kOtaFileContext, strlen(kOtaFileContext) + 1, 0) <
+        0) {
+        PLOG(ERROR) << "fsetxattr for the path: " << path << " failed";
+    }
+
     return true;
 }
 
@@ -2184,18 +2215,24 @@
  *
  */
 bool SnapshotManager::IsLegacySnapuserdPostReboot() {
-    if (is_legacy_snapuserd_.has_value() && is_legacy_snapuserd_.value() == true) {
-        auto slot = GetCurrentSlot();
-        if (slot == Slot::Target) {
-            // If this marker is present, then daemon can handle userspace
-            // snapshots; also, it indicates that the vendor partition was
-            // updated from Android 12.
-            if (access(GetSnapuserdFromSystemPath().c_str(), F_OK) == 0) {
-                return false;
-            }
+    auto slot = GetCurrentSlot();
+    if (slot == Slot::Target) {
+        /*
+            If this marker is present, the daemon can handle userspace snapshots.
+            During post-OTA reboot, this implies that the vendor partition is
+            Android 13 or higher. If the snapshots were created on an
+            Android 12 vendor, this means the vendor partition has been updated.
+        */
+        if (access(GetSnapuserdFromSystemPath().c_str(), F_OK) == 0) {
+            is_snapshot_userspace_ = true;
+            return false;
+        }
+        // If the marker isn't present and if the vendor is still in Android 12
+        if (is_legacy_snapuserd_.has_value() && is_legacy_snapuserd_.value() == true) {
             return true;
         }
     }
+
     return false;
 }
 
@@ -2865,10 +2902,12 @@
 }
 
 bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {
+    LOG(INFO) << "Lock acquired for " << __FUNCTION__;
     std::vector<std::string> snapshots;
     if (!ListSnapshots(lock, &snapshots)) {
         return false;
     }
+    LOG(INFO) << "Found " << snapshots.size() << " partitions with snapshots";
 
     for (const auto& snapshot : snapshots) {
         if (!UnmapPartitionWithSnapshot(lock, snapshot)) {
@@ -2892,6 +2931,7 @@
 
 auto SnapshotManager::OpenFile(const std::string& file,
                                int lock_flags) -> std::unique_ptr<LockedFile> {
+    const auto start = std::chrono::system_clock::now();
     unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
     if (fd < 0) {
         PLOG(ERROR) << "Open failed: " << file;
@@ -2904,6 +2944,11 @@
     // For simplicity, we want to CHECK that lock_mode == LOCK_EX, in some
     // calls, so strip extra flags.
     int lock_mode = lock_flags & (LOCK_EX | LOCK_SH);
+    const auto end = std::chrono::system_clock::now();
+    const auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
+    if (duration_ms >= 1000ms) {
+        LOG(INFO) << "Taking lock on " << file << " took " << duration_ms.count() << "ms";
+    }
     return std::make_unique<LockedFile>(file, std::move(fd), lock_mode);
 }
 
@@ -3056,6 +3101,7 @@
         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_cow_op_merge_size(old_status.cow_op_merge_size());
     }
     return WriteSnapshotUpdateStatus(lock, status);
 }
@@ -3438,6 +3484,8 @@
             status.set_legacy_snapuserd(true);
             LOG(INFO) << "Setting legacy_snapuserd to true";
         }
+        status.set_cow_op_merge_size(
+                android::base::GetUintProperty<uint32_t>("ro.virtual_ab.cow_op_merge_size", 0));
     } else if (legacy_compression) {
         LOG(INFO) << "Virtual A/B using legacy snapuserd";
     } else {
@@ -3873,6 +3921,7 @@
     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 << "Cow op merge size (0 for uncapped): " << update_status.cow_op_merge_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;
@@ -3956,44 +4005,90 @@
         // We allow the wipe to continue, because if we can't mount /metadata,
         // it is unlikely the device would have booted anyway. If there is no
         // metadata partition, then the device predates Virtual A/B.
+        LOG(INFO) << "/metadata not found; allowing wipe.";
         return true;
     }
 
-    // Check this early, so we don't accidentally start trying to populate
-    // the state file in recovery. Note we don't call GetUpdateState since
-    // we want errors in acquiring the lock to be propagated, instead of
-    // returning UpdateState::None.
-    auto state_file = GetStateFilePath();
-    if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
-        return true;
-    }
-
-    auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
-    auto super_path = device_->GetSuperDevice(slot_number);
-    if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {
-        LOG(ERROR) << "Unable to map partitions to complete merge.";
-        return false;
-    }
-
-    auto process_callback = [&]() -> bool {
-        if (callback) {
-            callback();
+    // This could happen if /metadata mounted but there is no filesystem
+    // structure. Weird, but we have to assume there's no OTA pending, and
+    // thus we let the wipe proceed.
+    UpdateState state;
+    {
+        auto lock = LockExclusive();
+        if (!lock) {
+            LOG(ERROR) << "Unable to determine update state; allowing wipe.";
+            return true;
         }
-        return true;
-    };
 
-    in_factory_data_reset_ = true;
-    UpdateState state =
-            ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
-    in_factory_data_reset_ = false;
-
-    if (state == UpdateState::MergeFailed) {
-        return false;
+        state = ReadUpdateState(lock.get());
+        LOG(INFO) << "Update state before wipe: " << state << "; slot: " << GetCurrentSlot()
+                  << "; suffix: " << device_->GetSlotSuffix();
     }
 
-    // Nothing should be depending on partitions now, so unmap them all.
-    if (!UnmapAllPartitionsInRecovery()) {
-        LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+    bool try_merge = false;
+    switch (state) {
+        case UpdateState::None:
+        case UpdateState::Initiated:
+            LOG(INFO) << "Wipe is not impacted by update state; allowing wipe.";
+            break;
+        case UpdateState::Unverified:
+            if (GetCurrentSlot() != Slot::Target) {
+                LOG(INFO) << "Wipe is not impacted by rolled back update; allowing wipe";
+                break;
+            }
+            if (!HasForwardMergeIndicator()) {
+                auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+                auto other_slot_number = SlotNumberForSlotSuffix(device_->GetOtherSlotSuffix());
+
+                // We're not allowed to forward merge, so forcefully rollback the
+                // slot switch.
+                LOG(INFO) << "Allowing wipe due to lack of forward merge indicator; reverting to "
+                             "old slot since update will be deleted.";
+                device_->SetSlotAsUnbootable(slot_number);
+                device_->SetActiveBootSlot(other_slot_number);
+                break;
+            }
+
+            // Forward merge indicator means we have to mount snapshots and try to merge.
+            LOG(INFO) << "Forward merge indicator is present.";
+            try_merge = true;
+            break;
+        case UpdateState::Merging:
+        case UpdateState::MergeFailed:
+            try_merge = true;
+            break;
+        case UpdateState::MergeNeedsReboot:
+        case UpdateState::Cancelled:
+            LOG(INFO) << "Unexpected update state in recovery; allowing wipe.";
+            break;
+        default:
+            break;
+    }
+
+    if (try_merge) {
+        auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+        auto super_path = device_->GetSuperDevice(slot_number);
+        if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {
+            LOG(ERROR) << "Unable to map partitions to complete merge.";
+            return false;
+        }
+
+        auto process_callback = [&]() -> bool {
+            if (callback) {
+                callback();
+            }
+            return true;
+        };
+
+        state = ProcessUpdateStateOnDataWipe(process_callback);
+        if (state == UpdateState::MergeFailed) {
+            return false;
+        }
+
+        // Nothing should be depending on partitions now, so unmap them all.
+        if (!UnmapAllPartitionsInRecovery()) {
+            LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+        }
     }
 
     if (state != UpdateState::None) {
@@ -4039,58 +4134,40 @@
     return true;
 }
 
-UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
-                                                          const std::function<bool()>& callback) {
-    auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
-    UpdateState state = ProcessUpdateState(callback);
-    LOG(INFO) << "Update state in recovery: " << state;
-    switch (state) {
-        case UpdateState::MergeFailed:
-            LOG(ERROR) << "Unrecoverable merge failure detected.";
-            return state;
-        case UpdateState::Unverified: {
-            // If an OTA was just applied but has not yet started merging:
-            //
-            // - if forward merge is allowed, initiate merge and call
-            // ProcessUpdateState again.
-            //
-            // - if forward merge is not allowed, we
-            // have no choice but to revert slots, because the current slot will
-            // immediately become unbootable. Rather than wait for the device
-            // to reboot N times until a rollback, we proactively disable the
-            // new slot instead.
-            //
-            // Since the rollback is inevitable, we don't treat a HAL failure
-            // as an error here.
-            auto slot = GetCurrentSlot();
-            if (slot == Slot::Target) {
-                if (allow_forward_merge &&
-                    access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0) {
-                    LOG(INFO) << "Forward merge allowed, initiating merge now.";
-
-                    if (!InitiateMerge()) {
-                        LOG(ERROR) << "Failed to initiate merge on data wipe.";
-                        return UpdateState::MergeFailed;
-                    }
-                    return ProcessUpdateStateOnDataWipe(false /* allow_forward_merge */, callback);
+UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(const std::function<bool()>& callback) {
+    while (true) {
+        UpdateState state = ProcessUpdateState(callback);
+        LOG(INFO) << "Processed updated state in recovery: " << state;
+        switch (state) {
+            case UpdateState::MergeFailed:
+                LOG(ERROR) << "Unrecoverable merge failure detected.";
+                return state;
+            case UpdateState::Unverified: {
+                // Unverified was already handled earlier, in HandleImminentDataWipe,
+                // but it will fall through here if a forward merge is required.
+                //
+                // If InitiateMerge fails, we early return. If it succeeds, then we
+                // are guaranteed that the next call to ProcessUpdateState will not
+                // return Unverified.
+                if (!InitiateMerge()) {
+                    LOG(ERROR) << "Failed to initiate merge on data wipe.";
+                    return UpdateState::MergeFailed;
                 }
-
-                LOG(ERROR) << "Reverting to old slot since update will be deleted.";
-                device_->SetSlotAsUnbootable(slot_number);
-            } else {
-                LOG(INFO) << "Booting from " << slot << " slot, no action is taken.";
+                continue;
             }
-            break;
+            case UpdateState::MergeNeedsReboot:
+                // We shouldn't get here, because nothing is depending on
+                // logical partitions.
+                LOG(ERROR) << "Unexpected merge-needs-reboot state in recovery.";
+                return state;
+            default:
+                return state;
         }
-        case UpdateState::MergeNeedsReboot:
-            // We shouldn't get here, because nothing is depending on
-            // logical partitions.
-            LOG(ERROR) << "Unexpected merge-needs-reboot state in recovery.";
-            break;
-        default:
-            break;
     }
-    return state;
+}
+
+bool SnapshotManager::HasForwardMergeIndicator() {
+    return access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0;
 }
 
 bool SnapshotManager::EnsureNoOverflowSnapshot(LockedFile* lock) {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 1435b12..16c247f 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -122,6 +122,7 @@
         LOG(INFO) << "Starting test: " << test_name_;
 
         SKIP_IF_NON_VIRTUAL_AB();
+        SKIP_IF_VENDOR_ON_ANDROID_S();
 
         SetupProperties();
         if (!DeviceSupportsMode()) {
@@ -168,6 +169,7 @@
 
     void TearDown() override {
         RETURN_IF_NON_VIRTUAL_AB();
+        RETURN_IF_VENDOR_ON_ANDROID_S();
 
         LOG(INFO) << "Tearing down SnapshotTest test: " << test_name_;
 
@@ -1015,6 +1017,7 @@
   public:
     void SetUp() override {
         SKIP_IF_NON_VIRTUAL_AB();
+        SKIP_IF_VENDOR_ON_ANDROID_S();
 
         SnapshotTest::SetUp();
         if (!image_manager_) {
@@ -1097,6 +1100,7 @@
     }
     void TearDown() override {
         RETURN_IF_NON_VIRTUAL_AB();
+        RETURN_IF_VENDOR_ON_ANDROID_S();
 
         LOG(INFO) << "Tearing down SnapshotUpdateTest test: " << test_name_;
 
@@ -2098,10 +2102,10 @@
     test_device->set_recovery(true);
     auto new_sm = NewManagerForFirstStageMount(test_device);
 
+    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
     ASSERT_TRUE(new_sm->HandleImminentDataWipe());
     // Manually mount metadata so that we can call GetUpdateState() below.
     MountMetadata();
-    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
     EXPECT_TRUE(test_device->IsSlotUnbootable(1));
     EXPECT_FALSE(test_device->IsSlotUnbootable(0));
 }
@@ -2123,6 +2127,7 @@
     test_device->set_recovery(true);
     auto new_sm = NewManagerForFirstStageMount(test_device);
 
+    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
     ASSERT_TRUE(new_sm->HandleImminentDataWipe());
     EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
     EXPECT_FALSE(test_device->IsSlotUnbootable(0));
@@ -2131,10 +2136,6 @@
 
 // Test update package that requests data wipe.
 TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
-    if (ShouldSkipLegacyMerging()) {
-        GTEST_SKIP() << "Skipping legacy merge in test";
-    }
-
     AddOperationForPartitions();
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
@@ -2153,6 +2154,7 @@
     test_device->set_recovery(true);
     auto new_sm = NewManagerForFirstStageMount(test_device);
 
+    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
     ASSERT_TRUE(new_sm->HandleImminentDataWipe());
     // Manually mount metadata so that we can call GetUpdateState() below.
     MountMetadata();
@@ -2174,10 +2176,6 @@
 
 // Test update package that requests data wipe.
 TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
-    if (ShouldSkipLegacyMerging()) {
-        GTEST_SKIP() << "Skipping legacy merge in test";
-    }
-
     AddOperationForPartitions();
 
     // Execute the update.
@@ -2218,6 +2216,7 @@
     test_device->set_recovery(true);
     auto new_sm = NewManagerForFirstStageMount(test_device);
 
+    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
     ASSERT_TRUE(new_sm->HandleImminentDataWipe());
     // Manually mount metadata so that we can call GetUpdateState() below.
     MountMetadata();
@@ -2661,6 +2660,7 @@
     status.set_o_direct(true);
     status.set_io_uring_enabled(true);
     status.set_userspace_snapshots(true);
+    status.set_cow_op_merge_size(16);
 
     sm->WriteSnapshotUpdateStatus(lock_.get(), status);
     // Ensure a connection to the second-stage daemon, but use the first-stage
@@ -2682,6 +2682,8 @@
                 snapuserd_argv.end());
     ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), "-user_snapshot") !=
                 snapuserd_argv.end());
+    ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), "-cow_op_merge_size=16") !=
+                snapuserd_argv.end());
 }
 
 class FlashAfterUpdateTest : public SnapshotUpdateTest,
@@ -2887,6 +2889,8 @@
 
 void SnapshotTestEnvironment::TearDown() {
     RETURN_IF_NON_VIRTUAL_AB();
+    RETURN_IF_VENDOR_ON_ANDROID_S();
+
     if (super_images_ != nullptr) {
         DeleteBackingImage(super_images_.get(), "fake-super");
     }
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index b3a7e8c..efbcb5a 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -267,6 +267,10 @@
     test_suites: [
         "vts",
     ],
+    test_options: {
+        // VABC mandatory in Android T per VSR.
+        min_shipping_api_level: 32,
+    },
 }
 
 cc_binary_host {
diff --git a/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp b/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp
index e988335..4599ad3 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp
@@ -27,11 +27,12 @@
 DmUserBlockServer::DmUserBlockServer(const std::string& misc_name, unique_fd&& ctrl_fd,
                                      Delegate* delegate, size_t buffer_size)
     : misc_name_(misc_name), ctrl_fd_(std::move(ctrl_fd)), delegate_(delegate) {
-    buffer_.Initialize(buffer_size);
+    buffer_.Initialize(sizeof(struct dm_user_header), buffer_size);
 }
 
 bool DmUserBlockServer::ProcessRequests() {
-    struct dm_user_header* header = buffer_.GetHeaderPtr();
+    struct dm_user_header* header =
+            reinterpret_cast<struct dm_user_header*>(buffer_.GetHeaderPtr());
     if (!android::base::ReadFully(ctrl_fd_, header, sizeof(*header))) {
         if (errno != ENOTBLK) {
             SNAP_PLOG(ERROR) << "Control-read failed";
@@ -90,7 +91,8 @@
 }
 
 void DmUserBlockServer::SendError() {
-    struct dm_user_header* header = buffer_.GetHeaderPtr();
+    struct dm_user_header* header =
+            reinterpret_cast<struct dm_user_header*>(buffer_.GetHeaderPtr());
     header->type = DM_USER_RESP_ERROR;
     // This is an issue with the dm-user interface. There
     // is no way to propagate the I/O error back to dm-user
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h
index f1f8da1..35c6bfb 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h
@@ -20,6 +20,7 @@
 
 #include <snapuserd/block_server.h>
 #include <snapuserd/snapuserd_buffer.h>
+#include <snapuserd/snapuserd_kernel.h>
 
 namespace android {
 namespace snapshot {
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
index c5ca2b1..cc7c48c 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
@@ -27,13 +27,17 @@
 
 class BufferSink final {
   public:
-    void Initialize(size_t size);
+    // Do not reserve any space of header by default
+    void Initialize(size_t size) { return Initialize(0, size); };
+    // This allows to set const header_size_ to be used if caller needs it
+    // for example, while working with dm_user
+    void Initialize(size_t header_size, size_t size);
     void* GetBufPtr() { return buffer_.get(); }
     void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
     void* GetPayloadBuffer(size_t size);
     void* GetBuffer(size_t requested, size_t* actual);
     void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
-    struct dm_user_header* GetHeaderPtr();
+    void* GetHeaderPtr();
     void ResetBufferOffset() { buffer_offset_ = 0; }
     void* GetPayloadBufPtr();
     loff_t GetPayloadBytesWritten() { return buffer_offset_; }
@@ -56,6 +60,7 @@
     std::unique_ptr<uint8_t[]> buffer_;
     loff_t buffer_offset_;
     size_t buffer_size_;
+    size_t header_size_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
index 7ab75dc..14291b2 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
@@ -92,15 +92,5 @@
     __u64 len;
 } __attribute__((packed));
 
-struct dm_user_payload {
-    __u8 buf[];
-};
-
-// Message comprising both header and payload
-struct dm_user_message {
-    struct dm_user_header header;
-    struct dm_user_payload payload;
-};
-
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
index 490c0e6..51b2490 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
@@ -22,8 +22,9 @@
 namespace android {
 namespace snapshot {
 
-void BufferSink::Initialize(size_t size) {
-    buffer_size_ = size + sizeof(struct dm_user_header);
+void BufferSink::Initialize(size_t header_size, size_t size) {
+    header_size_ = header_size;
+    buffer_size_ = size + header_size;
     buffer_offset_ = 0;
     buffer_ = std::make_unique<uint8_t[]>(buffer_size_);
 }
@@ -41,11 +42,11 @@
 
 void* BufferSink::GetPayloadBuffer(size_t size) {
     char* buffer = reinterpret_cast<char*>(GetBufPtr());
-    struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-    if ((buffer_size_ - buffer_offset_ - sizeof(msg->header)) < size) {
+
+    if ((buffer_size_ - buffer_offset_ - header_size_) < size) {
         return nullptr;
     }
-    return (char*)msg->payload.buf + buffer_offset_;
+    return (char*)(&buffer[0] + header_size_ + buffer_offset_);
 }
 
 void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
@@ -58,19 +59,18 @@
     return buf;
 }
 
-struct dm_user_header* BufferSink::GetHeaderPtr() {
-    if (!(sizeof(struct dm_user_header) <= buffer_size_)) {
+void* BufferSink::GetHeaderPtr() {
+    // If no sufficient space or header not reserved
+    if (!(header_size_ <= buffer_size_) || !header_size_) {
         return nullptr;
     }
     char* buf = reinterpret_cast<char*>(GetBufPtr());
-    struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
-    return header;
+    return (void*)(&(buf[0]));
 }
 
 void* BufferSink::GetPayloadBufPtr() {
     char* buffer = reinterpret_cast<char*>(GetBufPtr());
-    struct dm_user_message* msg = reinterpret_cast<struct dm_user_message*>(&(buffer[0]));
-    return msg->payload.buf;
+    return &buffer[header_size_];
 }
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index 67e9e52..dd2dd56 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -30,6 +30,7 @@
 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_int32(cow_op_merge_size, 0, "number of operations to be processed at once");
 
 namespace android {
 namespace snapshot {
@@ -106,7 +107,6 @@
         }
         return user_server_.Run();
     }
-
     for (int i = arg_start; i < argc; i++) {
         auto parts = android::base::Split(argv[i], ",");
 
@@ -114,8 +114,8 @@
             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_o_direct);
+        auto handler = user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3],
+                                               FLAGS_o_direct, FLAGS_cow_op_merge_size);
         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 c85331b..ef4ba93 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
@@ -41,7 +41,7 @@
 bool Extractor::Init() {
     auto opener = factory_.CreateTestOpener(control_name_);
     handler_ = std::make_shared<SnapshotHandler>(control_name_, cow_path_, base_path_, base_path_,
-                                                 opener, 1, false, false, false);
+                                                 opener, 1, false, false, false, 0);
     if (!handler_->InitCowDevice()) {
         return false;
     }
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 ea11f0e..fdd9cce 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
@@ -53,10 +53,10 @@
         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) {
+        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);
+            use_iouring, perform_verification_, o_direct, cow_op_merge_size);
     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 f23f07e..ecf5d5c 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
@@ -52,13 +52,11 @@
     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) = 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, int num_worker_threads, bool use_iouring,
+            bool o_direct, uint32_t cow_op_merge_size) = 0;
 
     // Start serving requests on a snapshot handler.
     virtual bool StartHandler(const std::string& misc_name) = 0;
@@ -98,7 +96,7 @@
                                               const std::string& base_path_merge,
                                               std::shared_ptr<IBlockServerOpener> opener,
                                               int num_worker_threads, bool use_iouring,
-                                              bool o_direct) override;
+                                              bool o_direct, uint32_t cow_op_merge_size) 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/merge_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
index e6a9a29..e2c5874 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
@@ -32,12 +32,18 @@
 
 MergeWorker::MergeWorker(const std::string& cow_device, const std::string& misc_name,
                          const std::string& base_path_merge,
-                         std::shared_ptr<SnapshotHandler> snapuserd)
-    : Worker(cow_device, misc_name, base_path_merge, snapuserd) {}
+                         std::shared_ptr<SnapshotHandler> snapuserd, uint32_t cow_op_merge_size)
+    : Worker(cow_device, misc_name, base_path_merge, snapuserd),
+      cow_op_merge_size_(cow_op_merge_size) {}
 
 int MergeWorker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
                               std::vector<const CowOperation*>* replace_zero_vec) {
     int num_ops = *pending_ops;
+    // 0 indicates ro.virtual_ab.cow_op_merge_size was not set in the build
+    if (cow_op_merge_size_ != 0) {
+        num_ops = std::min(cow_op_merge_size_, static_cast<uint32_t>(*pending_ops));
+    }
+
     int nr_consecutive = 0;
     bool checkOrderedOp = (replace_zero_vec == nullptr);
     size_t num_blocks = 1;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h
index 478d4c8..a19352d 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h
@@ -23,7 +23,8 @@
 class MergeWorker : public Worker {
   public:
     MergeWorker(const std::string& cow_device, const std::string& misc_name,
-                const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
+                const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd,
+                uint32_t cow_op_merge_size);
     bool Run();
 
   private:
@@ -53,6 +54,7 @@
     // syscalls and fallback to synchronous I/O, we
     // don't want huge queue depth
     int queue_depth_ = 8;
+    uint32_t cow_op_merge_size_ = 0;
 };
 
 }  // namespace snapshot
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 05ba047..7c9a64e 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -36,7 +36,8 @@
 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) {
+                                 bool use_iouring, bool perform_verification, bool o_direct,
+                                 uint32_t cow_op_merge_size) {
     misc_name_ = std::move(misc_name);
     cow_device_ = std::move(cow_device);
     backing_store_device_ = std::move(backing_device);
@@ -46,6 +47,7 @@
     is_io_uring_enabled_ = use_iouring;
     perform_verification_ = perform_verification;
     o_direct_ = o_direct;
+    cow_op_merge_size_ = cow_op_merge_size;
 }
 
 bool SnapshotHandler::InitializeWorkers() {
@@ -60,12 +62,11 @@
 
         worker_threads_.push_back(std::move(wt));
     }
-
     merge_thread_ = std::make_unique<MergeWorker>(cow_device_, misc_name_, base_path_merge_,
-                                                  GetSharedPtr());
+                                                  GetSharedPtr(), cow_op_merge_size_);
 
     read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
-                                                     GetSharedPtr());
+                                                     GetSharedPtr(), cow_op_merge_size_);
 
     update_verify_ = std::make_unique<UpdateVerify>(misc_name_);
 
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 9b7238a..c7de995 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -104,7 +104,8 @@
   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);
+                    int num_workers, bool use_iouring, bool perform_verification, bool o_direct,
+                    uint32_t cow_op_merge_size);
     bool InitCowDevice();
     bool Start();
 
@@ -247,7 +248,7 @@
     bool resume_merge_ = false;
     bool merge_complete_ = false;
     bool o_direct_ = false;
-
+    uint32_t cow_op_merge_size_ = 0;
     std::unique_ptr<UpdateVerify> update_verify_;
     std::shared_ptr<IBlockServerOpener> block_server_opener_;
 };
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index 2baf20d..6b1ed0c 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -18,6 +18,7 @@
 
 #include <pthread.h>
 
+#include "android-base/properties.h"
 #include "snapuserd_core.h"
 #include "utility.h"
 
@@ -29,11 +30,13 @@
 using android::base::unique_fd;
 
 ReadAhead::ReadAhead(const std::string& cow_device, const std::string& backing_device,
-                     const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd) {
+                     const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd,
+                     uint32_t cow_op_merge_size) {
     cow_device_ = cow_device;
     backing_store_device_ = backing_device;
     misc_name_ = misc_name;
     snapuserd_ = snapuserd;
+    cow_op_merge_size_ = cow_op_merge_size;
 }
 
 void ReadAhead::CheckOverlap(const CowOperation* cow_op) {
@@ -62,8 +65,11 @@
                                     std::vector<uint64_t>& blocks,
                                     std::vector<const CowOperation*>& xor_op_vec) {
     int num_ops = *pending_ops;
-    int nr_consecutive = 0;
+    if (cow_op_merge_size_ != 0) {
+        num_ops = std::min(static_cast<int>(cow_op_merge_size_), *pending_ops);
+    }
 
+    int nr_consecutive = 0;
     bool is_ops_present = (!RAIterDone() && num_ops);
 
     if (!is_ops_present) {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
index d3ba126..4885c96 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
@@ -35,7 +35,8 @@
 class ReadAhead {
   public:
     ReadAhead(const std::string& cow_device, const std::string& backing_device,
-              const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd);
+              const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd,
+              uint32_t cow_op_merge_size);
     bool RunThread();
 
   private:
@@ -106,6 +107,7 @@
     // syscalls and fallback to synchronous I/O, we
     // don't want huge queue depth
     int queue_depth_ = 8;
+    uint32_t cow_op_merge_size_;
     std::unique_ptr<struct io_uring> ring_;
 };
 
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 0b881b6..013df35 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -22,6 +22,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/socket.h>
+#include <sys/system_properties.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -35,9 +36,6 @@
 #include <snapuserd/snapuserd_client.h>
 #include "snapuserd_server.h"
 
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
 namespace android {
 namespace snapshot {
 
@@ -347,7 +345,8 @@
                                                               const std::string& cow_device_path,
                                                               const std::string& backing_device,
                                                               const std::string& base_path_merge,
-                                                              const bool o_direct) {
+                                                              const bool o_direct,
+                                                              uint32_t cow_op_merge_size) {
     // 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
@@ -369,7 +368,8 @@
     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, io_uring_enabled_, o_direct);
+                                 opener, num_worker_threads, io_uring_enabled_, o_direct,
+                                 cow_op_merge_size);
 }
 
 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 d9cf97f..ceea36a 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -87,7 +87,8 @@
                                               const std::string& cow_device_path,
                                               const std::string& backing_device,
                                               const std::string& base_path_merge,
-                                              bool o_direct = false);
+                                              bool o_direct = false,
+                                              uint32_t cow_op_merge_size = 0);
     bool StartHandler(const std::string& misc_name);
 
     void SetTerminating() { terminating_ = true; }
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 6d0ae3d..4dfb9bf 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -67,6 +67,7 @@
     std::string compression;
     int block_size;
     int num_threads;
+    uint32_t cow_op_merge_size;
 };
 
 class SnapuserdTestBase : public ::testing::TestWithParam<TestParam> {
@@ -708,9 +709,9 @@
     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);
+    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);
     ASSERT_NE(handler, nullptr);
     ASSERT_NE(handler->snapuserd(), nullptr);
 #ifdef __ANDROID__
@@ -1227,9 +1228,9 @@
     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);
+    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);
     ASSERT_TRUE(handler_->InitCowDevice());
     ASSERT_TRUE(handler_->InitializeWorkers());
 
@@ -1507,6 +1508,7 @@
                     param.num_threads = thread;
                     param.io_uring = io_uring;
                     param.o_direct = false;
+                    param.cow_op_merge_size = 0;
                     testParams.push_back(std::move(param));
                 }
             }
@@ -1528,6 +1530,14 @@
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
 
+#ifdef __ANDROID__
+    if (!android::snapshot::CanUseUserspaceSnapshots() ||
+        android::snapshot::IsVendorFromAndroid12()) {
+        std::cerr << "snapuserd_test not supported on this device\n";
+        return 0;
+    }
+#endif
+
     gflags::ParseCommandLineFlags(&argc, &argv, false);
 
     return RUN_ALL_TESTS();
diff --git a/fs_mgr/libsnapshot/snapuserd/utility.cpp b/fs_mgr/libsnapshot/snapuserd/utility.cpp
index 684ca3d..b44f5ab 100644
--- a/fs_mgr/libsnapshot/snapuserd/utility.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/utility.cpp
@@ -14,11 +14,14 @@
 
 #include "utility.h"
 
+#include <android-base/properties.h>
 #include <sys/resource.h>
 #include <sys/utsname.h>
 #include <unistd.h>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
+#include <libdm/dm.h>
 #include <processgroup/processgroup.h>
 
 #include <private/android_filesystem_config.h>
@@ -27,6 +30,7 @@
 namespace snapshot {
 
 using android::base::unique_fd;
+using android::dm::DeviceMapper;
 
 bool SetThreadPriority([[maybe_unused]] int priority) {
 #ifdef __ANDROID__
@@ -61,5 +65,38 @@
     return major > 5 || (major == 5 && minor >= 6);
 }
 
+bool GetUserspaceSnapshotsEnabledProperty() {
+    return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
+}
+
+bool KernelSupportsCompressedSnapshots() {
+    auto& dm = DeviceMapper::Instance();
+    return dm.GetTargetByName("user", nullptr);
+}
+
+bool IsVendorFromAndroid12() {
+    const std::string UNKNOWN = "unknown";
+    const std::string vendor_release =
+            android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
+
+    if (vendor_release.find("12") != std::string::npos) {
+        return true;
+    }
+    return false;
+}
+
+bool CanUseUserspaceSnapshots() {
+    if (!GetUserspaceSnapshotsEnabledProperty()) {
+        LOG(INFO) << "Virtual A/B - Userspace snapshots disabled";
+        return false;
+    }
+
+    if (!KernelSupportsCompressedSnapshots()) {
+        LOG(ERROR) << "Userspace snapshots requested, but no kernel support is available.";
+        return false;
+    }
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/utility.h b/fs_mgr/libsnapshot/snapuserd/utility.h
index c3c3cba..50be418 100644
--- a/fs_mgr/libsnapshot/snapuserd/utility.h
+++ b/fs_mgr/libsnapshot/snapuserd/utility.h
@@ -24,5 +24,10 @@
 bool SetProfiles(std::initializer_list<std::string_view> profiles);
 bool KernelSupportsIoUring();
 
+bool GetUserspaceSnapshotsEnabledProperty();
+bool KernelSupportsCompressedSnapshots();
+bool CanUseUserspaceSnapshots();
+bool IsVendorFromAndroid12();
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/tests/AndroidTest.xml b/fs_mgr/tests/AndroidTest.xml
index de835b3..1c06ebd 100644
--- a/fs_mgr/tests/AndroidTest.xml
+++ b/fs_mgr/tests/AndroidTest.xml
@@ -16,6 +16,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user_on_secondary_display" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 322bf1b..bd3d6b5 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1062,23 +1062,6 @@
             << "Default fstab doesn't contain /data entry";
 }
 
-TEST(fs_mgr, UserdataMountedFromDefaultFstab) {
-    if (getuid() != 0) {
-        GTEST_SKIP() << "Must be run as root.";
-        return;
-    }
-    Fstab fstab;
-    ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
-    Fstab proc_mounts;
-    ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &proc_mounts)) << "Failed to read /proc/mounts";
-    auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
-    ASSERT_NE(mounted_entry, nullptr) << "/data is not mounted";
-    std::string block_device;
-    ASSERT_TRUE(android::base::Realpath(mounted_entry->blk_device, &block_device));
-    ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, block_device))
-            << "/data wasn't mounted from default fstab";
-}
-
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Readahead_Size_KB) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 26af13b..54b469b 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -306,8 +306,8 @@
 
     if (!batt_anim_.run || now < next_screen_transition_) return;
 
-    // If battery level is not ready, keep checking in the defined time
-    if (health_info_.battery_level == 0 && health_info_.battery_status == BatteryStatus::UNKNOWN) {
+    // If battery status is not ready, keep checking in the defined time
+    if (health_info_.battery_status == BatteryStatus::UNKNOWN) {
         if (wait_batt_level_timestamp_ == 0) {
             // Set max delay time and skip drawing screen
             wait_batt_level_timestamp_ = now + MAX_BATT_LEVEL_WAIT_TIME;
@@ -317,7 +317,7 @@
             // Do nothing, keep waiting
             return;
         }
-        // If timeout and battery level is still not ready, draw unknown battery
+        // If timeout and battery status is still not ready, draw unknown battery
     }
 
     if (healthd_draw_ == nullptr) return;
diff --git a/init/Android.bp b/init/Android.bp
index c70a5de..ffb6380 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -330,7 +330,7 @@
     static_libs: ["libinit.microdroid"],
     cflags: ["-DMICRODROID=1"],
     no_full_install: true,
-    visibility: ["//packages/modules/Virtualization/microdroid"],
+    visibility: ["//packages/modules/Virtualization/build/microdroid"],
 }
 
 soong_config_module_type {
diff --git a/init/README.md b/init/README.md
index 11c4e1c..0bb26e8 100644
--- a/init/README.md
+++ b/init/README.md
@@ -499,9 +499,11 @@
    4. `late-fs` - Mount partitions marked as latemounted.
    5. `post-fs-data` - Mount and configure `/data`; set up encryption. `/metadata` is
       reformatted here if it couldn't mount in first-stage init.
-   6. `zygote-start` - Start the zygote.
-   7. `early-boot` - After zygote has started.
-   8. `boot` - After `early-boot` actions have completed.
+   6. `post-fs-data-checkpointed` - Triggered when vold has completed committing a checkpoint
+      after an OTA update. Not triggered if checkpointing is not needed or supported.
+   7. `zygote-start` - Start the zygote.
+   8. `early-boot` - After zygote has started.
+   9. `boot` - After `early-boot` actions have completed.
 
 Commands
 --------
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index a686d05..8f52158 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -139,6 +139,10 @@
     return InitDevice("/sys/devices/platform", dev_name);
 }
 
+bool BlockDevInitializer::InitHvcDevice(const std::string& dev_name) {
+    return InitDevice("/sys/devices/virtual/tty", dev_name);
+}
+
 bool BlockDevInitializer::InitDevice(const std::string& syspath, const std::string& device_name) {
     bool found = false;
 
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
index d5b1f60..cb1d365 100644
--- a/init/block_dev_initializer.h
+++ b/init/block_dev_initializer.h
@@ -34,6 +34,7 @@
     bool InitDevices(std::set<std::string> devices);
     bool InitDmDevice(const std::string& device);
     bool InitPlatformDevice(const std::string& device);
+    bool InitHvcDevice(const std::string& device);
 
   private:
     ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 606ea8c..122adb7 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -606,8 +606,6 @@
     return Error() << "Invalid code: " << code;
 }
 
-static int initial_mount_fstab_return_code = -1;
-
 /* <= Q: mount_all <fstab> [ <path> ]* [--<options>]*
  * >= R: mount_all [ <fstab> ] [--<options>]*
  *
@@ -648,19 +646,10 @@
         import_late(mount_all->rc_paths);
     }
 
-    if (mount_fstab_result.userdata_mounted) {
-        // This call to fs_mgr_mount_all mounted userdata. Keep the result in
-        // order for userspace reboot to correctly remount userdata.
-        LOG(INFO) << "Userdata mounted using "
-                  << (mount_all->fstab_path.empty() ? "(default fstab)" : mount_all->fstab_path)
-                  << " result : " << mount_fstab_result.code;
-        initial_mount_fstab_return_code = mount_fstab_result.code;
-    }
-
     if (queue_event) {
         /* queue_fs_event will queue event based on mount_fstab return code
          * and return processed return code*/
-        auto queue_fs_result = queue_fs_event(mount_fstab_result.code);
+        auto queue_fs_result = queue_fs_event(mount_fstab_result);
         if (!queue_fs_result.ok()) {
             return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
         }
@@ -1148,29 +1137,19 @@
 }
 
 static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
-    bool should_reboot_into_recovery = true;
     auto reboot_reason = vdc_arg + "_failed";
-    if (android::sysprop::InitProperties::userspace_reboot_in_progress().value_or(false)) {
-        should_reboot_into_recovery = false;
-        reboot_reason = "userspace_failed," + vdc_arg;
-    }
 
-    auto reboot = [reboot_reason, should_reboot_into_recovery](const std::string& message) {
+    auto reboot = [reboot_reason](const std::string& message) {
         // TODO (b/122850122): support this in gsi
-        if (should_reboot_into_recovery) {
-            if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
-                LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
-                if (auto result = reboot_into_recovery(
-                            {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
-                    !result.ok()) {
-                    LOG(FATAL) << "Could not reboot into recovery: " << result.error();
-                }
-            } else {
-                LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
+        if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
+            LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
+            if (auto result = reboot_into_recovery(
+                        {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+                !result.ok()) {
+                LOG(FATAL) << "Could not reboot into recovery: " << result.error();
             }
         } else {
-            LOG(ERROR) << message << ": rebooting, reason: " << reboot_reason;
-            trigger_shutdown("reboot," + reboot_reason);
+            LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
         }
     };
 
@@ -1178,29 +1157,6 @@
     return ExecWithFunctionOnFailure(args, reboot);
 }
 
-static Result<void> do_remount_userdata(const BuiltinArguments& args) {
-    if (initial_mount_fstab_return_code == -1) {
-        return Error() << "Calling remount_userdata too early";
-    }
-    Fstab fstab;
-    if (!ReadDefaultFstab(&fstab)) {
-        // TODO(b/135984674): should we reboot here?
-        return Error() << "Failed to read fstab";
-    }
-    // TODO(b/135984674): check that fstab contains /data.
-    if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
-        std::string proc_mounts_output;
-        android::base::ReadFileToString("/proc/mounts", &proc_mounts_output, true);
-        android::base::WriteStringToFile(proc_mounts_output,
-                                         "/metadata/userspacereboot/mount_info.txt");
-        trigger_shutdown("reboot,mount_userdata_failed");
-    }
-    if (auto result = queue_fs_event(initial_mount_fstab_return_code); !result.ok()) {
-        return Error() << "queue_fs_event() failed: " << result.error();
-    }
-    return {};
-}
-
 static Result<void> do_installkey(const BuiltinArguments& args) {
     if (!is_file_crypto()) return {};
 
@@ -1361,7 +1317,6 @@
         {"umount_all",              {0,     1,    {false,  do_umount_all}}},
         {"update_linker_config",    {0,     0,    {false,  do_update_linker_config}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
-        {"remount_userdata",        {0,     0,    {false,  do_remount_userdata}}},
         {"restart",                 {1,     2,    {false,  do_restart}}},
         {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
         {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 55cce6e..927b45f 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -305,6 +305,11 @@
             return false;
         }
     }
+
+    if (IsArcvm() && !block_dev_init_.InitHvcDevice("hvc1")) {
+        return false;
+    }
+
     return true;
 }
 
diff --git a/init/init.cpp b/init/init.cpp
index 19e909f..4878660 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -26,13 +26,11 @@
 #include <sys/eventfd.h>
 #include <sys/mount.h>
 #include <sys/signalfd.h>
+#include <sys/system_properties.h>
 #include <sys/types.h>
 #include <sys/utsname.h>
 #include <unistd.h>
 
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
 #include <filesystem>
 #include <fstream>
 #include <functional>
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 0d6eb15..96a9cd4 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -31,14 +31,12 @@
 #include <sys/mman.h>
 #include <sys/poll.h>
 #include <sys/select.h>
+#include <sys/system_properties.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <unistd.h>
 #include <wchar.h>
 
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
 #include <map>
 #include <memory>
 #include <mutex>
@@ -102,7 +100,6 @@
 using android::properties::ParsePropertyInfoFile;
 using android::properties::PropertyInfoAreaFile;
 using android::properties::PropertyInfoEntry;
-using android::sysprop::InitProperties::is_userspace_reboot_supported;
 
 namespace android {
 namespace init {
@@ -569,8 +566,8 @@
         }
         LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
                   << process_log_string;
-        if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
-            *error = "Userspace reboot is not supported by this device";
+        if (value == "reboot,userspace") {
+            *error = "Userspace reboot is deprecated.";
             return {PROP_ERROR_INVALID_VALUE};
         }
     }
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index 5f34cc4..c12ff72 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -16,11 +16,9 @@
 
 #include <errno.h>
 #include <sys/socket.h>
+#include <sys/system_properties.h>
 #include <sys/un.h>
 
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
 #include <android-base/properties.h>
 #include <android-base/scopeguard.h>
 #include <android-base/strings.h>
@@ -82,12 +80,6 @@
         GTEST_SKIP() << "Skipping test, must be run as root.";
         return;
     }
-    const std::string original_value = GetProperty("init.userspace_reboot.is_supported", "");
-    auto guard = android::base::make_scope_guard([&original_value]() {
-        SetProperty("init.userspace_reboot.is_supported", original_value);
-    });
-
-    ASSERT_TRUE(SetProperty("init.userspace_reboot.is_supported", "false"));
     EXPECT_FALSE(SetProperty("sys.powerctl", "reboot,userspace"));
 }
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 150f8f4..66ee977 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -87,16 +87,6 @@
 
 static const std::set<std::string> kDebuggingServices{"tombstoned", "logd", "adbd", "console"};
 
-static std::set<std::string> GetPostDataDebuggingServices() {
-    std::set<std::string> ret;
-    for (const auto& s : ServiceList::GetInstance()) {
-        if (kDebuggingServices.count(s->name()) && s->is_post_data()) {
-            ret.insert(s->name());
-        }
-    }
-    return ret;
-}
-
 static void PersistRebootReason(const char* reason, bool write_to_property) {
     if (write_to_property) {
         SetProperty(LAST_REBOOT_REASON_PROPERTY, reason);
@@ -810,196 +800,6 @@
     }
 }
 
-static void LeaveShutdown() {
-    LOG(INFO) << "Leaving shutdown mode";
-    shutting_down = false;
-    StartSendingMessages();
-}
-
-static std::chrono::milliseconds GetMillisProperty(const std::string& name,
-                                                   std::chrono::milliseconds default_value) {
-    auto value = GetUintProperty(name, static_cast<uint64_t>(default_value.count()));
-    return std::chrono::milliseconds(std::move(value));
-}
-
-static Result<void> DoUserspaceReboot() {
-    LOG(INFO) << "Userspace reboot initiated";
-    // An ugly way to pass a more precise reason on why fallback to hard reboot was triggered.
-    std::string sub_reason = "";
-    auto guard = android::base::make_scope_guard([&sub_reason] {
-        // Leave shutdown so that we can handle a full reboot.
-        LeaveShutdown();
-        trigger_shutdown("reboot,userspace_failed,shutdown_aborted," + sub_reason);
-    });
-    // Triggering userspace-reboot-requested will result in a bunch of setprop
-    // actions. We should make sure, that all of them are propagated before
-    // proceeding with userspace reboot. Synchronously setting sys.init.userspace_reboot.in_progress
-    // property is not perfect, but it should do the trick.
-    if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
-        sub_reason = "setprop";
-        return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
-    }
-    EnterShutdown();
-    if (!SetProperty("sys.powerctl", "")) {
-        sub_reason = "resetprop";
-        return Error() << "Failed to reset sys.powerctl property";
-    }
-    std::set<std::string> stop_first;
-    // Remember the services that were enabled. We will need to manually enable them again otherwise
-    // triggers like class_start won't restart them.
-    std::set<std::string> were_enabled;
-    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
-        if (s->is_post_data() && !kDebuggingServices.count(s->name())) {
-            stop_first.insert(s->name());
-        }
-        // TODO(ioffe): we should also filter out temporary services here.
-        if (s->is_post_data() && s->IsEnabled()) {
-            were_enabled.insert(s->name());
-        }
-    }
-    {
-        Timer sync_timer;
-        LOG(INFO) << "sync() before terminating services...";
-        sync();
-        LOG(INFO) << "sync() took " << sync_timer;
-    }
-    auto sigterm_timeout = GetMillisProperty("init.userspace_reboot.sigterm.timeoutmillis", 5s);
-    auto sigkill_timeout = GetMillisProperty("init.userspace_reboot.sigkill.timeoutmillis", 10s);
-    LOG(INFO) << "Timeout to terminate services: " << sigterm_timeout.count() << "ms "
-              << "Timeout to kill services: " << sigkill_timeout.count() << "ms";
-    std::string services_file_name = "/metadata/userspacereboot/services.txt";
-    const int flags = O_RDWR | O_CREAT | O_SYNC | O_APPEND | O_CLOEXEC;
-    StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
-    if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
-        r > 0) {
-        auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
-        android::base::WriteStringToFd("Post-data services still running: \n", fd);
-        for (const auto& s : ServiceList::GetInstance()) {
-            if (s->IsRunning() && stop_first.count(s->name())) {
-                android::base::WriteStringToFd(s->name() + "\n", fd);
-            }
-        }
-        sub_reason = "sigkill";
-        return Error() << r << " post-data services are still running";
-    }
-    if (auto result = KillZramBackingDevice(); !result.ok()) {
-        sub_reason = "zram";
-        return result;
-    }
-    if (auto result = CallVdc("volume", "reset"); !result.ok()) {
-        sub_reason = "vold_reset";
-        return result;
-    }
-    const auto& debugging_services = GetPostDataDebuggingServices();
-    if (int r = StopServicesAndLogViolations(debugging_services, sigkill_timeout,
-                                             false /* SIGKILL */);
-        r > 0) {
-        auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
-        android::base::WriteStringToFd("Debugging services still running: \n", fd);
-        for (const auto& s : ServiceList::GetInstance()) {
-            if (s->IsRunning() && debugging_services.count(s->name())) {
-                android::base::WriteStringToFd(s->name() + "\n", fd);
-            }
-        }
-        sub_reason = "sigkill_debug";
-        return Error() << r << " debugging services are still running";
-    }
-    {
-        Timer sync_timer;
-        LOG(INFO) << "sync() after stopping services...";
-        sync();
-        LOG(INFO) << "sync() took " << sync_timer;
-    }
-    if (auto result = UnmountAllApexes(); !result.ok()) {
-        sub_reason = "apex";
-        return result;
-    }
-    if (!SwitchToMountNamespaceIfNeeded(NS_BOOTSTRAP).ok()) {
-        sub_reason = "ns_switch";
-        return Error() << "Failed to switch to bootstrap namespace";
-    }
-    ActionManager::GetInstance().RemoveActionIf([](const auto& action) -> bool {
-        if (action->IsFromApex()) {
-            std::string trigger_name = action->BuildTriggersString();
-            LOG(INFO) << "Removing action (" << trigger_name << ") from (" << action->filename()
-                      << ":" << action->line() << ")";
-            return true;
-        }
-        return false;
-    });
-    // Remove services that were defined in an APEX
-    ServiceList::GetInstance().RemoveServiceIf([](const std::unique_ptr<Service>& s) -> bool {
-        if (s->is_from_apex()) {
-            LOG(INFO) << "Removing service '" << s->name() << "' because it's defined in an APEX";
-            return true;
-        }
-        return false;
-    });
-    // Re-enable services
-    for (const auto& s : ServiceList::GetInstance()) {
-        if (were_enabled.count(s->name())) {
-            LOG(INFO) << "Re-enabling service '" << s->name() << "'";
-            s->Enable();
-        }
-    }
-    ServiceList::GetInstance().ResetState();
-    LeaveShutdown();
-    ActionManager::GetInstance().QueueEventTrigger("userspace-reboot-resume");
-    guard.Disable();  // Go on with userspace reboot.
-    return {};
-}
-
-static void UserspaceRebootWatchdogThread() {
-    auto started_timeout = GetMillisProperty("init.userspace_reboot.started.timeoutmillis", 10s);
-    if (!WaitForProperty("sys.init.userspace_reboot.in_progress", "1", started_timeout)) {
-        LOG(ERROR) << "Userspace reboot didn't start in " << started_timeout.count()
-                   << "ms. Switching to full reboot";
-        // Init might be wedged, don't try to write reboot reason into a persistent property and do
-        // a dirty reboot.
-        PersistRebootReason("userspace_failed,watchdog_triggered,failed_to_start", false);
-        RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered,failed_to_start");
-    }
-    LOG(INFO) << "Starting userspace reboot watchdog";
-    auto watchdog_timeout = GetMillisProperty("init.userspace_reboot.watchdog.timeoutmillis", 5min);
-    LOG(INFO) << "UserspaceRebootWatchdog timeout: " << watchdog_timeout.count() << "ms";
-    if (!WaitForProperty("sys.boot_completed", "1", watchdog_timeout)) {
-        LOG(ERROR) << "Failed to boot in " << watchdog_timeout.count()
-                   << "ms. Switching to full reboot";
-        // In this case device is in a boot loop. Only way to recover is to do dirty reboot.
-        // Since init might be wedged, don't try to write reboot reason into a persistent property.
-        PersistRebootReason("userspace_failed,watchdog_triggered,failed_to_boot", false);
-        RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered,failed_to_boot");
-    }
-    LOG(INFO) << "Device booted, stopping userspace reboot watchdog";
-}
-
-static void HandleUserspaceReboot() {
-    if (!android::sysprop::InitProperties::is_userspace_reboot_supported().value_or(false)) {
-        LOG(ERROR) << "Attempted a userspace reboot on a device that doesn't support it";
-        return;
-    }
-    // Spinnig up a separate thread will fail the setns call later in the boot sequence.
-    // Fork a new process to monitor userspace reboot while we are investigating a better solution.
-    pid_t pid = fork();
-    if (pid < 0) {
-        PLOG(ERROR) << "Failed to fork process for userspace reboot watchdog. Switching to full "
-                    << "reboot";
-        trigger_shutdown("reboot,userspace_failed,watchdog_fork");
-        return;
-    }
-    if (pid == 0) {
-        // Child
-        UserspaceRebootWatchdogThread();
-        _exit(EXIT_SUCCESS);
-    }
-    LOG(INFO) << "Clearing queue and starting userspace-reboot-requested trigger";
-    auto& am = ActionManager::GetInstance();
-    am.ClearQueue();
-    am.QueueEventTrigger("userspace-reboot-requested");
-    auto handler = [](const BuiltinArguments&) { return DoUserspaceReboot(); };
-    am.QueueBuiltinAction(handler, "userspace-reboot");
-}
-
 /**
  * Check if "command" field is set in bootloader message.
  *
@@ -1030,7 +830,6 @@
     std::string reboot_target = "";
     bool run_fsck = false;
     bool command_invalid = false;
-    bool userspace_reboot = false;
 
     if (cmd_params[0] == "shutdown") {
         cmd = ANDROID_RB_POWEROFF;
@@ -1051,8 +850,8 @@
         if (cmd_params.size() >= 2) {
             reboot_target = cmd_params[1];
             if (reboot_target == "userspace") {
-                LOG(INFO) << "Userspace reboot requested";
-                userspace_reboot = true;
+                LOG(ERROR) << "Userspace reboot is deprecated.";
+                return;
             }
             // adb reboot fastboot should boot into bootloader for devices not
             // supporting logical partitions.
@@ -1130,11 +929,6 @@
     // messages, etc) from properties during reboot.
     StopSendingMessages();
 
-    if (userspace_reboot) {
-        HandleUserspaceReboot();
-        return;
-    }
-
     LOG(INFO) << "Clear action queue and start shutdown trigger";
     ActionManager::GetInstance().ClearQueue();
     // Queue shutdown trigger first
diff --git a/init/selinux.cpp b/init/selinux.cpp
index c2d9b8d..01af2b6 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -474,6 +474,8 @@
     RestoreconIfExists(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
     RestoreconIfExists("/metadata/gsi",
                        SELINUX_ANDROID_RESTORECON_RECURSE | SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);
+
+    RestoreconIfExists("/dev/hvc1", 0);
 }
 
 int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/init/util.h b/init/util.h
index aa24123..0565391 100644
--- a/init/util.h
+++ b/init/util.h
@@ -18,6 +18,7 @@
 
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/unistd.h>
 
 #include <chrono>
 #include <functional>
@@ -108,6 +109,10 @@
 #endif
 }
 
+inline bool IsArcvm() {
+    return !access("/is_arcvm", F_OK);
+}
+
 bool Has32BitAbi();
 
 std::string GetApexNameFromFileName(const std::string& path);
diff --git a/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump b/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump
index 67c7514..7ed131c 100644
--- a/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump
+++ b/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump
@@ -6,7 +6,6 @@
    "linker_set_key" : "_ZTIA0_i",
    "name" : "int[0]",
    "referenced_type" : "_ZTIi",
-   "self_type" : "_ZTIA0_i",
    "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
   }
  ],
@@ -17,8 +16,6 @@
    "is_integral" : true,
    "linker_set_key" : "_ZTIa",
    "name" : "signed char",
-   "referenced_type" : "_ZTIa",
-   "self_type" : "_ZTIa",
    "size" : 1
   },
   {
@@ -27,8 +24,6 @@
    "is_unsigned" : true,
    "linker_set_key" : "_ZTIb",
    "name" : "bool",
-   "referenced_type" : "_ZTIb",
-   "self_type" : "_ZTIb",
    "size" : 1
   },
   {
@@ -37,25 +32,27 @@
    "is_unsigned" : true,
    "linker_set_key" : "_ZTIc",
    "name" : "char",
-   "referenced_type" : "_ZTIc",
-   "self_type" : "_ZTIc",
    "size" : 1
   },
   {
    "alignment" : 4,
    "linker_set_key" : "_ZTIf",
    "name" : "float",
-   "referenced_type" : "_ZTIf",
-   "self_type" : "_ZTIf",
    "size" : 4
   },
   {
+   "alignment" : 1,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIh",
+   "name" : "unsigned char",
+   "size" : 1
+  },
+  {
    "alignment" : 4,
    "is_integral" : true,
    "linker_set_key" : "_ZTIi",
    "name" : "int",
-   "referenced_type" : "_ZTIi",
-   "self_type" : "_ZTIi",
    "size" : 4
   },
   {
@@ -64,8 +61,6 @@
    "is_unsigned" : true,
    "linker_set_key" : "_ZTIj",
    "name" : "unsigned int",
-   "referenced_type" : "_ZTIj",
-   "self_type" : "_ZTIj",
    "size" : 4
   },
   {
@@ -73,8 +68,6 @@
    "is_integral" : true,
    "linker_set_key" : "_ZTIl",
    "name" : "long",
-   "referenced_type" : "_ZTIl",
-   "self_type" : "_ZTIl",
    "size" : 8
   },
   {
@@ -83,15 +76,49 @@
    "is_unsigned" : true,
    "linker_set_key" : "_ZTIm",
    "name" : "unsigned long",
-   "referenced_type" : "_ZTIm",
-   "self_type" : "_ZTIm",
    "size" : 8
   },
   {
+   "alignment" : 16,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIo",
+   "name" : "unsigned __int128",
+   "size" : 16
+  },
+  {
+   "alignment" : 2,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIs",
+   "name" : "short",
+   "size" : 2
+  },
+  {
+   "alignment" : 2,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIt",
+   "name" : "unsigned short",
+   "size" : 2
+  },
+  {
    "linker_set_key" : "_ZTIv",
-   "name" : "void",
-   "referenced_type" : "_ZTIv",
-   "self_type" : "_ZTIv"
+   "name" : "void"
+  },
+  {
+   "alignment" : 8,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIx",
+   "name" : "long long",
+   "size" : 8
+  },
+  {
+   "alignment" : 8,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIy",
+   "name" : "unsigned long long",
+   "size" : 8
   }
  ],
  "elf_functions" :
@@ -100,70 +127,6 @@
    "name" : "_Z23socket_make_sockaddr_unPKciP11sockaddr_unPj"
   },
   {
-   "binding" : "weak",
-   "name" : "_ZN7android4base4TrimIRNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEES8_OT_"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPcl"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI9mbstate_tEEj"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__16vectorI5EntryNS_9allocatorIS1_EEE24__emplace_back_slow_pathIJS1_EEEvDpOT_"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__17getlineIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS_13basic_istreamIT_T0_EES9_RNS_12basic_stringIS6_S7_T1_EES6_"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc"
-  },
-  {
    "name" : "android_get_control_file"
   },
   {
@@ -488,6 +451,12 @@
    "name" : "str_parms_to_str"
   },
   {
+   "name" : "uevent_bind"
+  },
+  {
+   "name" : "uevent_create_socket"
+  },
+  {
    "name" : "uevent_kernel_multicast_recv"
   },
   {
@@ -503,22 +472,6 @@
  "elf_objects" :
  [
   {
-   "binding" : "weak",
-   "name" : "_ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
-  },
-  {
    "name" : "atrace_enabled_tags"
   },
   {
@@ -553,8 +506,6 @@
    ],
    "linker_set_key" : "_ZTI12IoSchedClass",
    "name" : "IoSchedClass",
-   "referenced_type" : "_ZTI12IoSchedClass",
-   "self_type" : "_ZTI12IoSchedClass",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h",
    "underlying_type" : "_ZTIj"
@@ -575,9 +526,7 @@
      "referenced_type" : "_ZTIPv"
     }
    ],
-   "referenced_type" : "_ZTIFbPvS_E",
    "return_type" : "_ZTIb",
-   "self_type" : "_ZTIFbPvS_E",
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
   {
@@ -596,9 +545,7 @@
      "referenced_type" : "_ZTIPv"
     }
    ],
-   "referenced_type" : "_ZTIFbPvS_S_E",
    "return_type" : "_ZTIb",
-   "self_type" : "_ZTIFbPvS_S_E",
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
   {
@@ -611,9 +558,7 @@
      "referenced_type" : "_ZTIPv"
     }
    ],
-   "referenced_type" : "_ZTIFiPvE",
    "return_type" : "_ZTIi",
-   "self_type" : "_ZTIFiPvE",
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
   {
@@ -632,9 +577,7 @@
      "referenced_type" : "_ZTIPv"
     }
    ],
-   "referenced_type" : "_ZTIFvPKcS0_PvE",
    "return_type" : "_ZTIv",
-   "self_type" : "_ZTIFvPKcS0_PvE",
    "source_file" : "system/core/libcutils/include/cutils/properties.h"
   }
  ],
@@ -2267,6 +2210,33 @@
    "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
   },
   {
+   "function_name" : "uevent_bind",
+   "linker_set_key" : "uevent_bind",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  },
+  {
+   "function_name" : "uevent_create_socket",
+   "linker_set_key" : "uevent_create_socket",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  },
+  {
    "function_name" : "uevent_kernel_multicast_recv",
    "linker_set_key" : "uevent_kernel_multicast_recv",
    "parameters" :
@@ -2374,7 +2344,6 @@
    "linker_set_key" : "_ZTIP12IoSchedClass",
    "name" : "IoSchedClass *",
    "referenced_type" : "_ZTI12IoSchedClass",
-   "self_type" : "_ZTIP12IoSchedClass",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
   },
@@ -2383,7 +2352,6 @@
    "linker_set_key" : "_ZTIP12RecordStream",
    "name" : "RecordStream *",
    "referenced_type" : "_ZTI12RecordStream",
-   "self_type" : "_ZTIP12RecordStream",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
   },
@@ -2392,7 +2360,6 @@
    "linker_set_key" : "_ZTIP13native_handle",
    "name" : "native_handle *",
    "referenced_type" : "_ZTI13native_handle",
-   "self_type" : "_ZTIP13native_handle",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
   },
@@ -2401,7 +2368,6 @@
    "linker_set_key" : "_ZTIP5cnode",
    "name" : "cnode *",
    "referenced_type" : "_ZTI5cnode",
-   "self_type" : "_ZTIP5cnode",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
   },
@@ -2410,7 +2376,6 @@
    "linker_set_key" : "_ZTIP7Hashmap",
    "name" : "Hashmap *",
    "referenced_type" : "_ZTI7Hashmap",
-   "self_type" : "_ZTIP7Hashmap",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
@@ -2419,7 +2384,6 @@
    "linker_set_key" : "_ZTIP9fs_config",
    "name" : "fs_config *",
    "referenced_type" : "_ZTI9fs_config",
-   "self_type" : "_ZTIP9fs_config",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/private/fs_config.h"
   },
@@ -2428,7 +2392,6 @@
    "linker_set_key" : "_ZTIP9str_parms",
    "name" : "str_parms *",
    "referenced_type" : "_ZTI9str_parms",
-   "self_type" : "_ZTIP9str_parms",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
   },
@@ -2437,7 +2400,6 @@
    "linker_set_key" : "_ZTIPFbPvS_E",
    "name" : "bool (*)(void *, void *)",
    "referenced_type" : "_ZTIFbPvS_E",
-   "self_type" : "_ZTIPFbPvS_E",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
@@ -2446,7 +2408,6 @@
    "linker_set_key" : "_ZTIPFbPvS_S_E",
    "name" : "bool (*)(void *, void *, void *)",
    "referenced_type" : "_ZTIFbPvS_S_E",
-   "self_type" : "_ZTIPFbPvS_S_E",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
@@ -2455,7 +2416,6 @@
    "linker_set_key" : "_ZTIPFiPvE",
    "name" : "int (*)(void *)",
    "referenced_type" : "_ZTIFiPvE",
-   "self_type" : "_ZTIPFiPvE",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
@@ -2464,7 +2424,6 @@
    "linker_set_key" : "_ZTIPFvPKcS0_PvE",
    "name" : "void (*)(const char *, const char *, void *)",
    "referenced_type" : "_ZTIFvPKcS0_PvE",
-   "self_type" : "_ZTIPFvPKcS0_PvE",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/properties.h"
   },
@@ -2473,7 +2432,6 @@
    "linker_set_key" : "_ZTIPK13native_handle",
    "name" : "const native_handle *",
    "referenced_type" : "_ZTIK13native_handle",
-   "self_type" : "_ZTIPK13native_handle",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
   },
@@ -2482,7 +2440,6 @@
    "linker_set_key" : "_ZTIPK22cutils_socket_buffer_t",
    "name" : "const cutils_socket_buffer_t *",
    "referenced_type" : "_ZTIK22cutils_socket_buffer_t",
-   "self_type" : "_ZTIPK22cutils_socket_buffer_t",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/sockets.h"
   },
@@ -2491,7 +2448,6 @@
    "linker_set_key" : "_ZTIPK5iovec",
    "name" : "const iovec *",
    "referenced_type" : "_ZTIK5iovec",
-   "self_type" : "_ZTIPK5iovec",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/klog.h"
   },
@@ -2500,7 +2456,6 @@
    "linker_set_key" : "_ZTIPKc",
    "name" : "const char *",
    "referenced_type" : "_ZTIKc",
-   "self_type" : "_ZTIPKc",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
   },
@@ -2509,7 +2464,6 @@
    "linker_set_key" : "_ZTIPKv",
    "name" : "const void *",
    "referenced_type" : "_ZTIKv",
-   "self_type" : "_ZTIPKv",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/sockets.h"
   },
@@ -2518,7 +2472,6 @@
    "linker_set_key" : "_ZTIPPv",
    "name" : "void **",
    "referenced_type" : "_ZTIPv",
-   "self_type" : "_ZTIPPv",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
   },
@@ -2527,7 +2480,6 @@
    "linker_set_key" : "_ZTIPc",
    "name" : "char *",
    "referenced_type" : "_ZTIc",
-   "self_type" : "_ZTIPc",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
   },
@@ -2536,7 +2488,6 @@
    "linker_set_key" : "_ZTIPf",
    "name" : "float *",
    "referenced_type" : "_ZTIf",
-   "self_type" : "_ZTIPf",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
   },
@@ -2545,7 +2496,6 @@
    "linker_set_key" : "_ZTIPi",
    "name" : "int *",
    "referenced_type" : "_ZTIi",
-   "self_type" : "_ZTIPi",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
   },
@@ -2554,7 +2504,6 @@
    "linker_set_key" : "_ZTIPj",
    "name" : "unsigned int *",
    "referenced_type" : "_ZTIj",
-   "self_type" : "_ZTIPj",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/misc.h"
   },
@@ -2563,7 +2512,6 @@
    "linker_set_key" : "_ZTIPm",
    "name" : "unsigned long *",
    "referenced_type" : "_ZTIm",
-   "self_type" : "_ZTIPm",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
   },
@@ -2572,7 +2520,6 @@
    "linker_set_key" : "_ZTIPv",
    "name" : "void *",
    "referenced_type" : "_ZTIv",
-   "self_type" : "_ZTIPv",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/misc.h"
   }
@@ -2585,7 +2532,6 @@
    "linker_set_key" : "_ZTIK13native_handle",
    "name" : "const native_handle",
    "referenced_type" : "_ZTI13native_handle",
-   "self_type" : "_ZTIK13native_handle",
    "size" : 12,
    "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
   },
@@ -2595,7 +2541,6 @@
    "linker_set_key" : "_ZTIK22cutils_socket_buffer_t",
    "name" : "const cutils_socket_buffer_t",
    "referenced_type" : "_ZTI22cutils_socket_buffer_t",
-   "self_type" : "_ZTIK22cutils_socket_buffer_t",
    "size" : 16,
    "source_file" : "system/core/libcutils/include/cutils/sockets.h"
   },
@@ -2605,7 +2550,6 @@
    "linker_set_key" : "_ZTIK5iovec",
    "name" : "const iovec",
    "referenced_type" : "_ZTI5iovec",
-   "self_type" : "_ZTIK5iovec",
    "size" : 16,
    "source_file" : "system/core/libcutils/include/cutils/klog.h"
   },
@@ -2615,7 +2559,6 @@
    "linker_set_key" : "_ZTIKc",
    "name" : "const char",
    "referenced_type" : "_ZTIc",
-   "self_type" : "_ZTIKc",
    "size" : 1,
    "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
   },
@@ -2624,7 +2567,6 @@
    "linker_set_key" : "_ZTIKv",
    "name" : "const void",
    "referenced_type" : "_ZTIv",
-   "self_type" : "_ZTIKv",
    "source_file" : "system/core/libcutils/include/cutils/sockets.h"
   }
  ],
@@ -2656,8 +2598,6 @@
    ],
    "linker_set_key" : "_ZTI13native_handle",
    "name" : "native_handle",
-   "referenced_type" : "_ZTI13native_handle",
-   "self_type" : "_ZTI13native_handle",
    "size" : 12,
    "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
   },
@@ -2677,8 +2617,6 @@
    ],
    "linker_set_key" : "_ZTI22cutils_socket_buffer_t",
    "name" : "cutils_socket_buffer_t",
-   "referenced_type" : "_ZTI22cutils_socket_buffer_t",
-   "self_type" : "_ZTI22cutils_socket_buffer_t",
    "size" : 16,
    "source_file" : "system/core/libcutils/include/cutils/sockets.h"
   },
@@ -2713,8 +2651,6 @@
    ],
    "linker_set_key" : "_ZTI5cnode",
    "name" : "cnode",
-   "referenced_type" : "_ZTI5cnode",
-   "self_type" : "_ZTI5cnode",
    "size" : 40,
    "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
   },
@@ -2744,8 +2680,6 @@
    ],
    "linker_set_key" : "_ZTI9fs_config",
    "name" : "fs_config",
-   "referenced_type" : "_ZTI9fs_config",
-   "self_type" : "_ZTI9fs_config",
    "size" : 24,
    "source_file" : "system/core/libcutils/include/private/fs_config.h"
   }
diff --git a/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump b/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump
index f75240c..fe4361a 100644
--- a/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump
+++ b/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump
@@ -6,7 +6,6 @@
    "linker_set_key" : "_ZTIA0_i",
    "name" : "int[0]",
    "referenced_type" : "_ZTIi",
-   "self_type" : "_ZTIA0_i",
    "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
   }
  ],
@@ -17,8 +16,6 @@
    "is_integral" : true,
    "linker_set_key" : "_ZTIa",
    "name" : "signed char",
-   "referenced_type" : "_ZTIa",
-   "self_type" : "_ZTIa",
    "size" : 1
   },
   {
@@ -27,8 +24,6 @@
    "is_unsigned" : true,
    "linker_set_key" : "_ZTIb",
    "name" : "bool",
-   "referenced_type" : "_ZTIb",
-   "self_type" : "_ZTIb",
    "size" : 1
   },
   {
@@ -37,25 +32,27 @@
    "is_unsigned" : true,
    "linker_set_key" : "_ZTIc",
    "name" : "char",
-   "referenced_type" : "_ZTIc",
-   "self_type" : "_ZTIc",
    "size" : 1
   },
   {
    "alignment" : 4,
    "linker_set_key" : "_ZTIf",
    "name" : "float",
-   "referenced_type" : "_ZTIf",
-   "self_type" : "_ZTIf",
    "size" : 4
   },
   {
+   "alignment" : 1,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIh",
+   "name" : "unsigned char",
+   "size" : 1
+  },
+  {
    "alignment" : 4,
    "is_integral" : true,
    "linker_set_key" : "_ZTIi",
    "name" : "int",
-   "referenced_type" : "_ZTIi",
-   "self_type" : "_ZTIi",
    "size" : 4
   },
   {
@@ -64,33 +61,47 @@
    "is_unsigned" : true,
    "linker_set_key" : "_ZTIj",
    "name" : "unsigned int",
-   "referenced_type" : "_ZTIj",
-   "self_type" : "_ZTIj",
    "size" : 4
   },
   {
+   "alignment" : 4,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIl",
+   "name" : "long",
+   "size" : 4
+  },
+  {
+   "alignment" : 4,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIm",
+   "name" : "unsigned long",
+   "size" : 4
+  },
+  {
+   "alignment" : 2,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIs",
+   "name" : "short",
+   "size" : 2
+  },
+  {
    "alignment" : 2,
    "is_integral" : true,
    "is_unsigned" : true,
    "linker_set_key" : "_ZTIt",
    "name" : "unsigned short",
-   "referenced_type" : "_ZTIt",
-   "self_type" : "_ZTIt",
    "size" : 2
   },
   {
    "linker_set_key" : "_ZTIv",
-   "name" : "void",
-   "referenced_type" : "_ZTIv",
-   "self_type" : "_ZTIv"
+   "name" : "void"
   },
   {
    "alignment" : 8,
    "is_integral" : true,
    "linker_set_key" : "_ZTIx",
    "name" : "long long",
-   "referenced_type" : "_ZTIx",
-   "self_type" : "_ZTIx",
    "size" : 8
   },
   {
@@ -99,8 +110,6 @@
    "is_unsigned" : true,
    "linker_set_key" : "_ZTIy",
    "name" : "unsigned long long",
-   "referenced_type" : "_ZTIy",
-   "self_type" : "_ZTIy",
    "size" : 8
   }
  ],
@@ -114,66 +123,6 @@
    "name" : "_ZN7android4base4TrimIRNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEES8_OT_"
   },
   {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPci"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI9mbstate_tEEj"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_j"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__16vectorI5EntryNS_9allocatorIS1_EEE24__emplace_back_slow_pathIJS1_EEEvDpOT_"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__17getlineIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS_13basic_istreamIT_T0_EES9_RNS_12basic_stringIS6_S7_T1_EES6_"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc"
-  },
-  {
    "name" : "android_get_control_file"
   },
   {
@@ -498,6 +447,12 @@
    "name" : "str_parms_to_str"
   },
   {
+   "name" : "uevent_bind"
+  },
+  {
+   "name" : "uevent_create_socket"
+  },
+  {
    "name" : "uevent_kernel_multicast_recv"
   },
   {
@@ -513,22 +468,6 @@
  "elf_objects" :
  [
   {
-   "binding" : "weak",
-   "name" : "_ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE"
-  },
-  {
-   "binding" : "weak",
-   "name" : "_ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
-  },
-  {
    "name" : "atrace_enabled_tags"
   },
   {
@@ -563,8 +502,6 @@
    ],
    "linker_set_key" : "_ZTI12IoSchedClass",
    "name" : "IoSchedClass",
-   "referenced_type" : "_ZTI12IoSchedClass",
-   "self_type" : "_ZTI12IoSchedClass",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h",
    "underlying_type" : "_ZTIj"
@@ -585,9 +522,7 @@
      "referenced_type" : "_ZTIPv"
     }
    ],
-   "referenced_type" : "_ZTIFbPvS_E",
    "return_type" : "_ZTIb",
-   "self_type" : "_ZTIFbPvS_E",
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
   {
@@ -606,9 +541,7 @@
      "referenced_type" : "_ZTIPv"
     }
    ],
-   "referenced_type" : "_ZTIFbPvS_S_E",
    "return_type" : "_ZTIb",
-   "self_type" : "_ZTIFbPvS_S_E",
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
   {
@@ -621,9 +554,7 @@
      "referenced_type" : "_ZTIPv"
     }
    ],
-   "referenced_type" : "_ZTIFiPvE",
    "return_type" : "_ZTIi",
-   "self_type" : "_ZTIFiPvE",
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
   {
@@ -642,9 +573,7 @@
      "referenced_type" : "_ZTIPv"
     }
    ],
-   "referenced_type" : "_ZTIFvPKcS0_PvE",
    "return_type" : "_ZTIv",
-   "self_type" : "_ZTIFvPKcS0_PvE",
    "source_file" : "system/core/libcutils/include/cutils/properties.h"
   }
  ],
@@ -2277,6 +2206,33 @@
    "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
   },
   {
+   "function_name" : "uevent_bind",
+   "linker_set_key" : "uevent_bind",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  },
+  {
+   "function_name" : "uevent_create_socket",
+   "linker_set_key" : "uevent_create_socket",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  },
+  {
    "function_name" : "uevent_kernel_multicast_recv",
    "linker_set_key" : "uevent_kernel_multicast_recv",
    "parameters" :
@@ -2384,7 +2340,6 @@
    "linker_set_key" : "_ZTIP12IoSchedClass",
    "name" : "IoSchedClass *",
    "referenced_type" : "_ZTI12IoSchedClass",
-   "self_type" : "_ZTIP12IoSchedClass",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
   },
@@ -2393,7 +2348,6 @@
    "linker_set_key" : "_ZTIP12RecordStream",
    "name" : "RecordStream *",
    "referenced_type" : "_ZTI12RecordStream",
-   "self_type" : "_ZTIP12RecordStream",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
   },
@@ -2402,7 +2356,6 @@
    "linker_set_key" : "_ZTIP13native_handle",
    "name" : "native_handle *",
    "referenced_type" : "_ZTI13native_handle",
-   "self_type" : "_ZTIP13native_handle",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
   },
@@ -2411,7 +2364,6 @@
    "linker_set_key" : "_ZTIP5cnode",
    "name" : "cnode *",
    "referenced_type" : "_ZTI5cnode",
-   "self_type" : "_ZTIP5cnode",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
   },
@@ -2420,7 +2372,6 @@
    "linker_set_key" : "_ZTIP7Hashmap",
    "name" : "Hashmap *",
    "referenced_type" : "_ZTI7Hashmap",
-   "self_type" : "_ZTIP7Hashmap",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
@@ -2429,7 +2380,6 @@
    "linker_set_key" : "_ZTIP9fs_config",
    "name" : "fs_config *",
    "referenced_type" : "_ZTI9fs_config",
-   "self_type" : "_ZTIP9fs_config",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/private/fs_config.h"
   },
@@ -2438,7 +2388,6 @@
    "linker_set_key" : "_ZTIP9str_parms",
    "name" : "str_parms *",
    "referenced_type" : "_ZTI9str_parms",
-   "self_type" : "_ZTIP9str_parms",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
   },
@@ -2447,7 +2396,6 @@
    "linker_set_key" : "_ZTIPFbPvS_E",
    "name" : "bool (*)(void *, void *)",
    "referenced_type" : "_ZTIFbPvS_E",
-   "self_type" : "_ZTIPFbPvS_E",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
@@ -2456,7 +2404,6 @@
    "linker_set_key" : "_ZTIPFbPvS_S_E",
    "name" : "bool (*)(void *, void *, void *)",
    "referenced_type" : "_ZTIFbPvS_S_E",
-   "self_type" : "_ZTIPFbPvS_S_E",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
@@ -2465,7 +2412,6 @@
    "linker_set_key" : "_ZTIPFiPvE",
    "name" : "int (*)(void *)",
    "referenced_type" : "_ZTIFiPvE",
-   "self_type" : "_ZTIPFiPvE",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
   },
@@ -2474,7 +2420,6 @@
    "linker_set_key" : "_ZTIPFvPKcS0_PvE",
    "name" : "void (*)(const char *, const char *, void *)",
    "referenced_type" : "_ZTIFvPKcS0_PvE",
-   "self_type" : "_ZTIPFvPKcS0_PvE",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/properties.h"
   },
@@ -2483,7 +2428,6 @@
    "linker_set_key" : "_ZTIPK13native_handle",
    "name" : "const native_handle *",
    "referenced_type" : "_ZTIK13native_handle",
-   "self_type" : "_ZTIPK13native_handle",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
   },
@@ -2492,7 +2436,6 @@
    "linker_set_key" : "_ZTIPK22cutils_socket_buffer_t",
    "name" : "const cutils_socket_buffer_t *",
    "referenced_type" : "_ZTIK22cutils_socket_buffer_t",
-   "self_type" : "_ZTIPK22cutils_socket_buffer_t",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/sockets.h"
   },
@@ -2501,7 +2444,6 @@
    "linker_set_key" : "_ZTIPK5iovec",
    "name" : "const iovec *",
    "referenced_type" : "_ZTIK5iovec",
-   "self_type" : "_ZTIPK5iovec",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/klog.h"
   },
@@ -2510,7 +2452,6 @@
    "linker_set_key" : "_ZTIPKc",
    "name" : "const char *",
    "referenced_type" : "_ZTIKc",
-   "self_type" : "_ZTIPKc",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
   },
@@ -2519,7 +2460,6 @@
    "linker_set_key" : "_ZTIPKv",
    "name" : "const void *",
    "referenced_type" : "_ZTIKv",
-   "self_type" : "_ZTIPKv",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/sockets.h"
   },
@@ -2528,7 +2468,6 @@
    "linker_set_key" : "_ZTIPPv",
    "name" : "void **",
    "referenced_type" : "_ZTIPv",
-   "self_type" : "_ZTIPPv",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
   },
@@ -2537,7 +2476,6 @@
    "linker_set_key" : "_ZTIPc",
    "name" : "char *",
    "referenced_type" : "_ZTIc",
-   "self_type" : "_ZTIPc",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
   },
@@ -2546,7 +2484,6 @@
    "linker_set_key" : "_ZTIPf",
    "name" : "float *",
    "referenced_type" : "_ZTIf",
-   "self_type" : "_ZTIPf",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
   },
@@ -2555,7 +2492,6 @@
    "linker_set_key" : "_ZTIPi",
    "name" : "int *",
    "referenced_type" : "_ZTIi",
-   "self_type" : "_ZTIPi",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
   },
@@ -2564,7 +2500,6 @@
    "linker_set_key" : "_ZTIPj",
    "name" : "unsigned int *",
    "referenced_type" : "_ZTIj",
-   "self_type" : "_ZTIPj",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/misc.h"
   },
@@ -2573,7 +2508,6 @@
    "linker_set_key" : "_ZTIPv",
    "name" : "void *",
    "referenced_type" : "_ZTIv",
-   "self_type" : "_ZTIPv",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/cutils/misc.h"
   },
@@ -2582,7 +2516,6 @@
    "linker_set_key" : "_ZTIPy",
    "name" : "unsigned long long *",
    "referenced_type" : "_ZTIy",
-   "self_type" : "_ZTIPy",
    "size" : 4,
    "source_file" : "system/core/libcutils/include/private/canned_fs_config.h"
   }
@@ -2595,7 +2528,6 @@
    "linker_set_key" : "_ZTIK13native_handle",
    "name" : "const native_handle",
    "referenced_type" : "_ZTI13native_handle",
-   "self_type" : "_ZTIK13native_handle",
    "size" : 12,
    "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
   },
@@ -2605,7 +2537,6 @@
    "linker_set_key" : "_ZTIK22cutils_socket_buffer_t",
    "name" : "const cutils_socket_buffer_t",
    "referenced_type" : "_ZTI22cutils_socket_buffer_t",
-   "self_type" : "_ZTIK22cutils_socket_buffer_t",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/sockets.h"
   },
@@ -2615,7 +2546,6 @@
    "linker_set_key" : "_ZTIK5iovec",
    "name" : "const iovec",
    "referenced_type" : "_ZTI5iovec",
-   "self_type" : "_ZTIK5iovec",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/klog.h"
   },
@@ -2625,7 +2555,6 @@
    "linker_set_key" : "_ZTIKc",
    "name" : "const char",
    "referenced_type" : "_ZTIc",
-   "self_type" : "_ZTIKc",
    "size" : 1,
    "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
   },
@@ -2634,7 +2563,6 @@
    "linker_set_key" : "_ZTIKv",
    "name" : "const void",
    "referenced_type" : "_ZTIv",
-   "self_type" : "_ZTIKv",
    "source_file" : "system/core/libcutils/include/cutils/sockets.h"
   }
  ],
@@ -2666,8 +2594,6 @@
    ],
    "linker_set_key" : "_ZTI13native_handle",
    "name" : "native_handle",
-   "referenced_type" : "_ZTI13native_handle",
-   "self_type" : "_ZTI13native_handle",
    "size" : 12,
    "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
   },
@@ -2687,8 +2613,6 @@
    ],
    "linker_set_key" : "_ZTI22cutils_socket_buffer_t",
    "name" : "cutils_socket_buffer_t",
-   "referenced_type" : "_ZTI22cutils_socket_buffer_t",
-   "self_type" : "_ZTI22cutils_socket_buffer_t",
    "size" : 8,
    "source_file" : "system/core/libcutils/include/cutils/sockets.h"
   },
@@ -2723,8 +2647,6 @@
    ],
    "linker_set_key" : "_ZTI5cnode",
    "name" : "cnode",
-   "referenced_type" : "_ZTI5cnode",
-   "self_type" : "_ZTI5cnode",
    "size" : 20,
    "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
   },
@@ -2754,8 +2676,6 @@
    ],
    "linker_set_key" : "_ZTI9fs_config",
    "name" : "fs_config",
-   "referenced_type" : "_ZTI9fs_config",
-   "self_type" : "_ZTI9fs_config",
    "size" : 24,
    "source_file" : "system/core/libcutils/include/private/fs_config.h"
   }
diff --git a/libcutils/include/cutils/uevent.h b/libcutils/include/cutils/uevent.h
index da1c2aa..1be981b 100644
--- a/libcutils/include/cutils/uevent.h
+++ b/libcutils/include/cutils/uevent.h
@@ -24,6 +24,8 @@
 extern "C" {
 #endif
 
+int uevent_create_socket(int buf_sz, bool passcred);
+int uevent_bind(int socket);
 int uevent_open_socket(int buf_sz, bool passcred);
 ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length);
 ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid);
diff --git a/libcutils/properties.cpp b/libcutils/properties.cpp
index 03f0496..d3a2b50 100644
--- a/libcutils/properties.cpp
+++ b/libcutils/properties.cpp
@@ -93,8 +93,7 @@
 
 #if __has_include(<sys/system_properties.h>)
 
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
+#include <sys/system_properties.h>
 
 struct callback_data {
     void (*callback)(const char* name, const char* value, void* cookie);
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index 3bc6dc3..c911b4f 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -36,8 +36,7 @@
 #include <log/log_properties.h>
 
 #if defined(__BIONIC__)
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
+#include <sys/system_properties.h>
 #endif
 
 /**
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
index 40bbd5c..f01a479 100644
--- a/libcutils/uevent.cpp
+++ b/libcutils/uevent.cpp
@@ -92,20 +92,22 @@
     return -1;
 }
 
-int uevent_open_socket(int buf_sz, bool passcred) {
-    struct sockaddr_nl addr;
-    int on = passcred;
+/*
+ * Creates an unbound netlink socket for receiving uevent messages.
+ * @buf_sz: socket receive buffer size.
+ * @passcred: whether or not to enable receiving the SCM_CREDENTIALS control
+ *	message.
+ *
+ * Returns: a socket descriptor upon success or -1 upon failure.
+ */
+int uevent_create_socket(int buf_sz, bool passcred) {
+    int s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
+    if (s < 0) {
+        return -1;
+    }
+
     int buf_sz_readback = 0;
     socklen_t optlen = sizeof(buf_sz_readback);
-    int s;
-
-    memset(&addr, 0, sizeof(addr));
-    addr.nl_family = AF_NETLINK;
-    addr.nl_pid = 0;
-    addr.nl_groups = 0xffffffff;
-
-    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
-    if (s < 0) return -1;
 
     if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0 ||
           getsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz_readback, &optlen) < 0) {
@@ -123,9 +125,43 @@
         }
     }
 
+    int on = passcred;
+
     setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
 
-    if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+    return s;
+}
+
+/*
+ * Binds a netlink socket. Binding a netlink socket makes the kernel start
+ * sending netlink messages to that netlink socket.
+ *
+ * Returns: 0 upon success; -1 upon error.
+ */
+int uevent_bind(int socket) {
+    struct sockaddr_nl addr = {
+            .nl_family = AF_NETLINK,
+            .nl_pid = 0,
+            .nl_groups = 0xffffffff,
+    };
+    return bind(socket, (struct sockaddr*)&addr, sizeof(addr));
+}
+
+/*
+ * Creates a bound netlink socket for receiving uevent messages.
+ * @buf_sz: socket receive buffer size.
+ * @passcred: whether or not to enable receiving the SCM_CREDENTIALS control
+ *	message.
+ *
+ * Returns: a socket descriptor upon success or -1 upon failure.
+ */
+int uevent_open_socket(int buf_sz, bool passcred) {
+    int s = uevent_create_socket(buf_sz, passcred);
+    if (s < 0) {
+        return -1;
+    }
+
+    if (uevent_bind(s) < 0) {
         close(s);
         return -1;
     }
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 1a40da1..8cc0b9b 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -447,7 +447,7 @@
 // Softdeps are taken care in InsmodWithDeps().
 bool Modprobe::LoadModulesParallel(int num_threads) {
     bool ret = true;
-    std::unordered_map<std::string, std::vector<std::string>> mod_with_deps;
+    std::map<std::string, std::vector<std::string>> mod_with_deps;
 
     // Get dependencies
     for (const auto& module : module_load_) {
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 52b5afe..fb01cfd 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -38,26 +38,26 @@
 static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
 static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.threads";
 
-uint32_t CgroupController::version() const {
+uint32_t CgroupControllerWrapper::version() const {
     CHECK(HasValue());
     return ACgroupController_getVersion(controller_);
 }
 
-const char* CgroupController::name() const {
+const char* CgroupControllerWrapper::name() const {
     CHECK(HasValue());
     return ACgroupController_getName(controller_);
 }
 
-const char* CgroupController::path() const {
+const char* CgroupControllerWrapper::path() const {
     CHECK(HasValue());
     return ACgroupController_getPath(controller_);
 }
 
-bool CgroupController::HasValue() const {
+bool CgroupControllerWrapper::HasValue() const {
     return controller_ != nullptr;
 }
 
-bool CgroupController::IsUsable() {
+bool CgroupControllerWrapper::IsUsable() {
     if (!HasValue()) return false;
 
     if (state_ == UNKNOWN) {
@@ -72,7 +72,7 @@
     return state_ == USABLE;
 }
 
-std::string CgroupController::GetTasksFilePath(const std::string& rel_path) const {
+std::string CgroupControllerWrapper::GetTasksFilePath(const std::string& rel_path) const {
     std::string tasks_path = path();
 
     if (!rel_path.empty()) {
@@ -81,8 +81,8 @@
     return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
 }
 
-std::string CgroupController::GetProcsFilePath(const std::string& rel_path, uid_t uid,
-                                               pid_t pid) const {
+std::string CgroupControllerWrapper::GetProcsFilePath(const std::string& rel_path, uid_t uid,
+                                                      pid_t pid) const {
     std::string proc_path(path());
     proc_path.append("/").append(rel_path);
     proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
@@ -91,7 +91,7 @@
     return proc_path.append(CGROUP_PROCS_FILE);
 }
 
-bool CgroupController::GetTaskGroup(pid_t tid, std::string* group) const {
+bool CgroupControllerWrapper::GetTaskGroup(pid_t tid, std::string* group) const {
     std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
     std::string content;
     if (!android::base::ReadFileToString(file_name, &content)) {
@@ -175,40 +175,40 @@
     }
 }
 
-CgroupController CgroupMap::FindController(const std::string& name) const {
+CgroupControllerWrapper CgroupMap::FindController(const std::string& name) const {
     if (!loaded_) {
         LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
                    << "] failed, RC file was not initialized properly";
-        return CgroupController(nullptr);
+        return CgroupControllerWrapper(nullptr);
     }
 
     auto controller_count = ACgroupFile_getControllerCount();
     for (uint32_t i = 0; i < controller_count; ++i) {
         const ACgroupController* controller = ACgroupFile_getController(i);
         if (name == ACgroupController_getName(controller)) {
-            return CgroupController(controller);
+            return CgroupControllerWrapper(controller);
         }
     }
 
-    return CgroupController(nullptr);
+    return CgroupControllerWrapper(nullptr);
 }
 
-CgroupController CgroupMap::FindControllerByPath(const std::string& path) const {
+CgroupControllerWrapper CgroupMap::FindControllerByPath(const std::string& path) const {
     if (!loaded_) {
         LOG(ERROR) << "CgroupMap::FindControllerByPath called for [" << getpid()
                    << "] failed, RC file was not initialized properly";
-        return CgroupController(nullptr);
+        return CgroupControllerWrapper(nullptr);
     }
 
     auto controller_count = ACgroupFile_getControllerCount();
     for (uint32_t i = 0; i < controller_count; ++i) {
         const ACgroupController* controller = ACgroupFile_getController(i);
         if (StartsWith(path, ACgroupController_getPath(controller))) {
-            return CgroupController(controller);
+            return CgroupControllerWrapper(controller);
         }
     }
 
-    return CgroupController(nullptr);
+    return CgroupControllerWrapper(nullptr);
 }
 
 int CgroupMap::ActivateControllers(const std::string& path) const {
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index 31925d5..3642794 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -23,10 +23,10 @@
 #include <android/cgrouprc.h>
 
 // Convenient wrapper of an ACgroupController pointer.
-class CgroupController {
+class CgroupControllerWrapper {
   public:
     // Does not own controller
-    explicit CgroupController(const ACgroupController* controller)
+    explicit CgroupControllerWrapper(const ACgroupController* controller)
         : controller_(controller) {}
 
     uint32_t version() const;
@@ -53,12 +53,9 @@
 
 class CgroupMap {
   public:
-    // Selinux policy ensures only init process can successfully use this function
-    static bool SetupCgroups();
-
     static CgroupMap& GetInstance();
-    CgroupController FindController(const std::string& name) const;
-    CgroupController FindControllerByPath(const std::string& path) const;
+    CgroupControllerWrapper FindController(const std::string& name) const;
+    CgroupControllerWrapper FindControllerByPath(const std::string& path) const;
     int ActivateControllers(const std::string& path) const;
 
   private:
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
index 7522cfe..cb91247 100644
--- a/libprocessgroup/cgrouprc/Android.bp
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -32,8 +32,8 @@
         symbol_file: "libcgrouprc.map.txt",
     },
     srcs: [
-        "cgroup_controller.cpp",
-        "cgroup_file.cpp",
+        "a_cgroup_controller.cpp",
+        "a_cgroup_file.cpp",
     ],
     cflags: [
         "-Wall",
diff --git a/libprocessgroup/cgrouprc/cgroup_controller.cpp b/libprocessgroup/cgrouprc/a_cgroup_controller.cpp
similarity index 100%
rename from libprocessgroup/cgrouprc/cgroup_controller.cpp
rename to libprocessgroup/cgrouprc/a_cgroup_controller.cpp
diff --git a/libprocessgroup/cgrouprc/cgroup_file.cpp b/libprocessgroup/cgrouprc/a_cgroup_file.cpp
similarity index 100%
rename from libprocessgroup/cgrouprc/cgroup_file.cpp
rename to libprocessgroup/cgrouprc/a_cgroup_file.cpp
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 387c104..83a2258 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -94,7 +94,7 @@
 }
 
 static bool CgroupGetMemcgAppsPath(std::string* path) {
-    CgroupController controller = CgroupMap::GetInstance().FindController("memory");
+    CgroupControllerWrapper controller = CgroupMap::GetInstance().FindController("memory");
 
     if (!controller.HasValue()) {
         return false;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 4870548..67ecc1d 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -123,8 +123,8 @@
     return file_name_;
 }
 
-void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name,
-                             const std::string& file_v2_name) {
+void ProfileAttribute::Reset(const CgroupControllerWrapper& controller,
+                             const std::string& file_name, const std::string& file_v2_name) {
     controller_ = controller;
     file_name_ = file_name;
     file_v2_name_ = file_v2_name;
@@ -333,7 +333,7 @@
     return optional_;
 }
 
-SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
+SetCgroupAction::SetCgroupAction(const CgroupControllerWrapper& c, const std::string& p)
     : controller_(c), path_(p) {
     FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);
     // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 184e9e3..abb3ca5 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -32,9 +32,9 @@
 class IProfileAttribute {
   public:
     virtual ~IProfileAttribute() = 0;
-    virtual void Reset(const CgroupController& controller, const std::string& file_name,
+    virtual void Reset(const CgroupControllerWrapper& controller, const std::string& file_name,
                        const std::string& file_v2_name) = 0;
-    virtual const CgroupController* controller() const = 0;
+    virtual const CgroupControllerWrapper* controller() const = 0;
     virtual const std::string& file_name() const = 0;
     virtual bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const = 0;
     virtual bool GetPathForTask(pid_t tid, std::string* path) const = 0;
@@ -46,14 +46,14 @@
     // Cgroup attributes may have different names in the v1 and v2 hierarchies. If `file_v2_name` is
     // not empty, `file_name` is the name for the v1 hierarchy and `file_v2_name` is the name for
     // the v2 hierarchy. If `file_v2_name` is empty, `file_name` is used for both hierarchies.
-    ProfileAttribute(const CgroupController& controller, const std::string& file_name,
+    ProfileAttribute(const CgroupControllerWrapper& controller, const std::string& file_name,
                      const std::string& file_v2_name)
         : controller_(controller), file_name_(file_name), file_v2_name_(file_v2_name) {}
     ~ProfileAttribute() = default;
 
-    const CgroupController* controller() const override { return &controller_; }
+    const CgroupControllerWrapper* controller() const override { return &controller_; }
     const std::string& file_name() const override;
-    void Reset(const CgroupController& controller, const std::string& file_name,
+    void Reset(const CgroupControllerWrapper& controller, const std::string& file_name,
                const std::string& file_v2_name) override;
 
     bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const override;
@@ -61,7 +61,7 @@
     bool GetPathForUID(uid_t uid, std::string* path) const override;
 
   private:
-    CgroupController controller_;
+    CgroupControllerWrapper controller_;
     std::string file_name_;
     std::string file_v2_name_;
 };
@@ -142,7 +142,7 @@
 // Set cgroup profile element
 class SetCgroupAction : public ProfileAction {
   public:
-    SetCgroupAction(const CgroupController& c, const std::string& p);
+    SetCgroupAction(const CgroupControllerWrapper& c, const std::string& p);
 
     const char* Name() const override { return "SetCgroup"; }
     bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
@@ -152,10 +152,10 @@
     bool IsValidForProcess(uid_t uid, pid_t pid) const override;
     bool IsValidForTask(pid_t tid) const override;
 
-    const CgroupController* controller() const { return &controller_; }
+    const CgroupControllerWrapper* controller() const { return &controller_; }
 
   private:
-    CgroupController controller_;
+    CgroupControllerWrapper controller_;
     std::string path_;
     android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
     mutable std::mutex fd_mutex_;
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
index d19da2b..dff6d67 100644
--- a/libprocessgroup/task_profiles_test.cpp
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -102,10 +102,10 @@
   public:
     ProfileAttributeMock(const std::string& file_name) : file_name_(file_name) {}
     ~ProfileAttributeMock() override = default;
-    void Reset(const CgroupController&, const std::string&, const std::string&) override {
+    void Reset(const CgroupControllerWrapper&, const std::string&, const std::string&) override {
         CHECK(false);
     }
-    const CgroupController* controller() const override {
+    const CgroupControllerWrapper* controller() const override {
         CHECK(false);
         return {};
     }
diff --git a/libutils/include/utils/Trace.h b/libutils/include/utils/Trace.h
index 9986bf5..1809100 100644
--- a/libutils/include/utils/Trace.h
+++ b/libutils/include/utils/Trace.h
@@ -17,13 +17,6 @@
 #ifndef ANDROID_TRACE_H
 #define ANDROID_TRACE_H
 
-#if defined(_WIN32)
-
-#define ATRACE_NAME(...)
-#define ATRACE_CALL()
-
-#else  // !_WIN32
-
 #include <stdint.h>
 
 #include <cutils/trace.h>
@@ -56,6 +49,4 @@
 
 }  // namespace android
 
-#endif  // _WIN32
-
 #endif // ANDROID_TRACE_H
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index e8f7627..1e7b48b 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -114,10 +114,78 @@
     sub_dir: "init",
 }
 
+prebuilt_etc {
+    name: "asan.options",
+    src: "asan.options",
+}
+
+sh_binary {
+    name: "asan_extract",
+    src: "asan_extract.sh",
+    init_rc: ["asan_extract.rc"],
+    // We need bzip2 on device for extraction.
+    required: ["bzip2"],
+}
+
 llndk_libraries_txt {
     name: "llndk.libraries.txt",
 }
 
 sanitizer_libraries_txt {
     name: "sanitizer.libraries.txt",
-}
\ No newline at end of file
+}
+
+EXPORT_GLOBAL_ASAN_OPTIONS = select(soong_config_variable("ANDROID", "ASAN_ENABLED"), {
+    true: "export ASAN_OPTIONS include=/system/asan.options",
+    default: "",
+})
+
+EXPORT_GLOBAL_HWASAN_OPTIONS = select(soong_config_variable("ANDROID", "HWASAN_ENABLED"), {
+    true: "export HWASAN_OPTIONS heap_history_size=1023,stack_history_size=512,export_memory_stats=0,max_malloc_fill_size=131072,malloc_fill_byte=0",
+    default: "",
+})
+
+EXPORT_GLOBAL_GCOV_OPTIONS = select(soong_config_variable("ANDROID", "GCOV_COVERAGE"), {
+    true: "export GCOV_PREFIX /data/misc/trace",
+    default: "",
+})
+
+EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS = select((soong_config_variable("ANDROID", "CLANG_COVERAGE"), soong_config_variable("ANDROID", "CLANG_COVERAGE_CONTINUOUS_MODE")), {
+    (true, true): "export LLVM_PROFILE_FILE /data/misc/trace/clang%c-%20m.profraw",
+    (true, default): "export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw",
+    (default, default): "",
+})
+
+EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE = select(soong_config_variable("ANDROID", "SCUDO_ALLOCATION_RING_BUFFER_SIZE"), {
+    "": "",
+    any @ size: "export SCUDO_ALLOCATION_RING_BUFFER_SIZE " + size,
+    default: "",
+})
+
+genrule {
+    name: "init.environ.rc.gen",
+    srcs: ["init.environ.rc.in"],
+    out: ["init.environ.rc"],
+    cmd: "cp -f $(in) $(out) && " +
+        "echo '    " + EXPORT_GLOBAL_ASAN_OPTIONS + "' >> $(out) && " +
+        "echo '    " + EXPORT_GLOBAL_GCOV_OPTIONS + "' >> $(out) && " +
+        "echo '    " + EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS + "' >> $(out) && " +
+        "echo '    " + EXPORT_GLOBAL_HWASAN_OPTIONS + "' >> $(out) && " +
+        "echo '    " + EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE + "' >> $(out)",
+}
+
+prebuilt_root {
+    name: "init.environ.rc-soong",
+    src: ":init.environ.rc.gen",
+    filename: "init.environ.rc",
+    install_in_root: true,
+    no_full_install: true,
+    required: select((soong_config_variable("ANDROID", "ASAN_ENABLED"), soong_config_variable("ANDROID", "SANITIZE_TARGET_SYSTEM_ENABLED")), {
+        (true, true): [
+            "asan.options",
+            "asan_extract",
+        ],
+        (true, default): ["asan.options"],
+        (default, default): [],
+    }),
+}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 4c1f2e4..e743743 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -3,41 +3,15 @@
 $(eval $(call declare-1p-copy-files,system/core/rootdir,))
 
 #######################################
-# asan.options
 ifneq ($(filter address,$(SANITIZE_TARGET)),)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := asan.options
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(TARGET_OUT)
-
-include $(BUILD_PREBUILT)
-
-# ASAN extration.
 ASAN_EXTRACT_FILES :=
 ifeq ($(SANITIZE_TARGET_SYSTEM),true)
-include $(CLEAR_VARS)
-LOCAL_MODULE:= asan_extract
-LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS:= notice
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_SRC_FILES := asan_extract.sh
-LOCAL_INIT_RC := asan_extract.rc
-# We need bzip2 on device for extraction.
-LOCAL_REQUIRED_MODULES := bzip2
-include $(BUILD_PREBUILT)
 ASAN_EXTRACT_FILES := asan_extract
 endif
-
 endif
-
 #######################################
 # init.environ.rc
+# TODO(b/353429422): move LOCAL_POST_INSTALL_CMD to other rules and remove Android.mk module.
 
 include $(CLEAR_VARS)
 LOCAL_MODULE_CLASS := ETC
@@ -46,36 +20,8 @@
 LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
 
-EXPORT_GLOBAL_ASAN_OPTIONS :=
 ifneq ($(filter address,$(SANITIZE_TARGET)),)
-  EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS include=/system/asan.options
-  LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES) $(ASAN_EXTRACT_FILES)
-endif
-
-EXPORT_GLOBAL_HWASAN_OPTIONS :=
-ifneq ($(filter hwaddress,$(SANITIZE_TARGET)),)
-  ifneq ($(HWADDRESS_SANITIZER_GLOBAL_OPTIONS),)
-    EXPORT_GLOBAL_HWASAN_OPTIONS := export HWASAN_OPTIONS $(HWADDRESS_SANITIZER_GLOBAL_OPTIONS)
-  endif
-endif
-
-EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE :=
-ifneq ($(PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE),)
-  EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE := export SCUDO_ALLOCATION_RING_BUFFER_SIZE $(PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE)
-endif
-
-EXPORT_GLOBAL_GCOV_OPTIONS :=
-ifeq ($(NATIVE_COVERAGE),true)
-  EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
-endif
-
-EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
-ifeq ($(CLANG_COVERAGE),true)
-  ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true)
-    EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang%c-%20m.profraw
-  else
-    EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
-  endif
+  LOCAL_REQUIRED_MODULES := asan.options $(ASAN_EXTRACT_FILES)
 endif
 
 # Put it here instead of in init.rc module definition,
@@ -200,15 +146,10 @@
 include $(BUILD_SYSTEM)/base_rules.mk
 
 $(ALL_ROOTDIR_SYMLINKS): $(LOCAL_BUILT_MODULE)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
-	@echo "Generate: $< -> $@"
-	@mkdir -p $(dir $@)
-	$(hide) cp $< $@
-	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
-	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
-	$(hide) sed -i -e 's?%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%?$(EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS)?g' $@
-	$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
-	$(hide) sed -i -e 's?%EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE%?$(EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE)?g' $@
+
+init.environ.rc-soong := $(call intermediates-dir-for,ETC,init.environ.rc-soong)/init.environ.rc-soong
+$(eval $(call copy-one-file,$(init.environ.rc-soong),$(LOCAL_BUILT_MODULE)))
+init.environ.rc-soong :=
 
 #######################################
 # ramdisk_node_list
diff --git a/rootdir/avb/Android.bp b/rootdir/avb/Android.bp
new file mode 100644
index 0000000..a584e3e
--- /dev/null
+++ b/rootdir/avb/Android.bp
@@ -0,0 +1,71 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+soong_config_module_type {
+    name: "avb_keys_prebuilt_avb",
+    module_type: "prebuilt_avb",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT",
+    ],
+    properties: [
+        "ramdisk",
+        "vendor_ramdisk",
+    ],
+}
+
+avb_keys_prebuilt_avb {
+    name: "q-developer-gsi.avbpubkey",
+    src: "q-developer-gsi.avbpubkey",
+    soong_config_variables: {
+        BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT: {
+            ramdisk: false,
+            vendor_ramdisk: true,
+            conditions_default: {
+                ramdisk: true,
+                vendor_ramdisk: false,
+            },
+        },
+    },
+}
+
+avb_keys_prebuilt_avb {
+    name: "r-developer-gsi.avbpubkey",
+    src: "r-developer-gsi.avbpubkey",
+    soong_config_variables: {
+        BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT: {
+            ramdisk: false,
+            vendor_ramdisk: true,
+            conditions_default: {
+                ramdisk: true,
+                vendor_ramdisk: false,
+            },
+        },
+    },
+}
+
+avb_keys_prebuilt_avb {
+    name: "s-developer-gsi.avbpubkey",
+    src: "s-developer-gsi.avbpubkey",
+    soong_config_variables: {
+        BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT: {
+            ramdisk: false,
+            vendor_ramdisk: true,
+            conditions_default: {
+                ramdisk: true,
+                vendor_ramdisk: false,
+            },
+        },
+    },
+}
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
deleted file mode 100644
index 8cf3172..0000000
--- a/rootdir/avb/Android.mk
+++ /dev/null
@@ -1,56 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-ifeq ($(BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT),true) # AVB keys are installed to vendor ramdisk
-  ifeq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true) # no dedicated recovery partition
-    my_gsi_avb_keys_path := $(TARGET_VENDOR_RAMDISK_OUT)/first_stage_ramdisk/avb
-  else # device has a dedicated recovery partition
-    my_gsi_avb_keys_path := $(TARGET_VENDOR_RAMDISK_OUT)/avb
-  endif
-else
-  ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) # no dedicated recovery partition
-    my_gsi_avb_keys_path := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
-  else # device has a dedicated recovery partition
-    my_gsi_avb_keys_path := $(TARGET_RAMDISK_OUT)/avb
-  endif
-endif
-
-#######################################
-# q-developer-gsi.avbpubkey
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := q-developer-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
-
-include $(BUILD_PREBUILT)
-
-#######################################
-# r-developer-gsi.avbpubkey
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := r-developer-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
-
-include $(BUILD_PREBUILT)
-
-#######################################
-# s-developer-gsi.avbpubkey
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := s-developer-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
-
-include $(BUILD_PREBUILT)
-
-my_gsi_avb_keys_path :=
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 7ba1f46..9249270 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -10,8 +10,5 @@
     export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
     export EXTERNAL_STORAGE /sdcard
     export ASEC_MOUNTPOINT /mnt/asec
-    %EXPORT_GLOBAL_ASAN_OPTIONS%
-    %EXPORT_GLOBAL_GCOV_OPTIONS%
-    %EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%
-    %EXPORT_GLOBAL_HWASAN_OPTIONS%
-    %EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE%
+    # Additional environment variables will be appended here during build (see Android.bp).
+    # DO NOT ADD additional sections like 'on <event>' here.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 2443b7c..e867998 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -620,7 +620,6 @@
     mkdir /metadata/bootstat 0750 system log
     mkdir /metadata/ota 0750 root system
     mkdir /metadata/ota/snapshots 0750 root system
-    mkdir /metadata/userspacereboot 0770 root system
     mkdir /metadata/watchdog 0770 root system
 
     mkdir /metadata/apex 0700 root system
@@ -648,6 +647,14 @@
     chmod 0755 /sys/kernel/tracing
     chmod 0755 /sys/kernel/debug/tracing
 
+    # Early HALs may use early VM. Mount /mnt/vm before starting such HALs.
+    mkdir /mnt/vm 0755 root root
+    mount tmpfs tmpfs /mnt/vm nosuid nodev noexec rw
+    restorecon /mnt/vm
+    chown system system /mnt/vm
+    chmod 0770 /mnt/vm
+    mkdir /mnt/vm/early 0770 system system
+
     # HALs required before storage encryption can get unlocked (FBE)
     class_start early_hal
 
@@ -1087,6 +1094,9 @@
     # Update dm-verity state and set partition.*.verified properties.
     verity_update_state
 
+on property:vold.checkpoint_committed=1
+    trigger post-fs-data-checkpointed
+
 # It is recommended to put unnecessary data/ initialization from post-fs-data
 # to start-zygote in device's init.rc to unblock zygote start.
 on zygote-start
@@ -1320,46 +1330,6 @@
 on init && property:ro.debuggable=1
     start console
 
-on userspace-reboot-requested
-  # TODO(b/135984674): reset all necessary properties here.
-  setprop sys.boot_completed ""
-  setprop dev.bootcomplete ""
-  setprop sys.init.updatable_crashing ""
-  setprop sys.init.updatable_crashing_process_name ""
-  setprop sys.user.0.ce_available ""
-  setprop sys.shutdown.requested ""
-  setprop service.bootanim.exit ""
-  setprop service.bootanim.progress ""
-
-on userspace-reboot-fs-remount
-  # Make sure that vold is running.
-  # This is mostly a precaution measure in case vold for some reason wasn't running when
-  # userspace reboot was initiated.
-  start vold
-  exec - system system -- /system/bin/vdc checkpoint resetCheckpoint
-  exec - system system -- /system/bin/vdc checkpoint markBootAttempt
-  # Unmount /data_mirror mounts in the reverse order of corresponding mounts.
-  umount /data_mirror/data_ce/null/0
-  umount /data_mirror/data_ce/null
-  umount /data_mirror/data_de/null
-  umount /data_mirror/storage_area/0
-  umount /data_mirror/storage_area
-  umount /data_mirror/cur_profiles
-  umount /data_mirror/ref_profiles
-  umount /data_mirror
-  remount_userdata
-  start bootanim
-
-on userspace-reboot-resume
-  trigger userspace-reboot-fs-remount
-  trigger post-fs-data
-  trigger zygote-start
-  trigger early-boot
-  trigger boot
-
-on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
-  setprop sys.init.userspace_reboot.in_progress ""
-
 # Multi-Gen LRU Experiment
 on property:persist.device_config.mglru_native.lru_gen_config=none
   write /sys/kernel/mm/lru_gen/enabled 0
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index dde784e..b30d6d0 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -139,7 +139,3 @@
 on property:sys.usb.typec.power_role=sink
     write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role}
     setprop sys.usb.typec.state ${sys.usb.typec.power_role}
-
-on userspace-reboot-requested
-  setprop sys.usb.config ""
-  setprop sys.usb.state ""
diff --git a/trusty/gatekeeper/service.cpp b/trusty/gatekeeper/service.cpp
index d09804f..59366b8 100644
--- a/trusty/gatekeeper/service.cpp
+++ b/trusty/gatekeeper/service.cpp
@@ -18,12 +18,66 @@
 #include <android-base/logging.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
+#include <getopt.h>
 
 #include "trusty_gatekeeper.h"
+#include "trusty_gatekeeper_ipc.h"
 
 using aidl::android::hardware::gatekeeper::TrustyGateKeeperDevice;
 
-int main() {
+static const char* _sopts = "hD:";
+static const struct option _lopts[] = {
+        {"help", no_argument, 0, 'h'},
+        {"dev", required_argument, 0, 'D'},
+        {0, 0, 0, 0},
+};
+
+static const char* usage =
+        "Usage: %s [options]\n"
+        "\n"
+        "options:\n"
+        "  -h, --help            prints this message and exit\n"
+        "  -D, --dev name        Trusty device name\n"
+        "\n";
+
+static const char* usage_long = "\n";
+
+static void print_usage_and_exit(const char* prog, int code, bool verbose) {
+    fprintf(stderr, usage, prog);
+    if (verbose) {
+        fprintf(stderr, "%s", usage_long);
+    }
+    exit(code);
+}
+
+static void parse_options(int argc, char** argv) {
+    int c;
+    int oidx = 0;
+
+    while (1) {
+        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
+        if (c == -1) {
+            break; /* done */
+        }
+
+        switch (c) {
+            case 'D':
+                trusty_gatekeeper_set_dev_name(optarg);
+                break;
+
+            case 'h':
+                print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
+                break;
+
+            default:
+                print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+        }
+    }
+}
+
+int main(int argc, char** argv) {
+    parse_options(argc, argv);
+
     ABinderProcess_setThreadPoolMaxThreadCount(0);
 
     std::shared_ptr<TrustyGateKeeperDevice> gatekeeper =
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.c b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
index f67944b..5ca951c 100644
--- a/trusty/gatekeeper/trusty_gatekeeper_ipc.c
+++ b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
@@ -28,12 +28,15 @@
 #include "trusty_gatekeeper_ipc.h"
 #include "gatekeeper_ipc.h"
 
-#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
-
+static const char* trusty_device_name = "/dev/trusty-ipc-dev0";
 static int handle_ = 0;
 
+void trusty_gatekeeper_set_dev_name(const char* device_name) {
+    trusty_device_name = device_name;
+}
+
 int trusty_gatekeeper_connect() {
-    int rc = tipc_connect(TRUSTY_DEVICE_NAME, GATEKEEPER_PORT);
+    int rc = tipc_connect(trusty_device_name, GATEKEEPER_PORT);
     if (rc < 0) {
         return rc;
     }
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.h b/trusty/gatekeeper/trusty_gatekeeper_ipc.h
index f8de7f8..47ba33b 100644
--- a/trusty/gatekeeper/trusty_gatekeeper_ipc.h
+++ b/trusty/gatekeeper/trusty_gatekeeper_ipc.h
@@ -16,6 +16,7 @@
 
 __BEGIN_DECLS
 
+void trusty_gatekeeper_set_dev_name(const char* device_name);
 int trusty_gatekeeper_connect();
 int trusty_gatekeeper_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
                            uint32_t *out_size);
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index b249013..aca59b6 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -44,7 +44,7 @@
         "libtrusty",
         "libkeymaster_messages",
         "libkeymaster3device",
-        "android.hardware.keymaster@3.0"
+        "android.hardware.keymaster@3.0",
     ],
 }
 
@@ -74,7 +74,7 @@
         "libtrusty",
         "libkeymaster_messages",
         "libkeymaster4",
-        "android.hardware.keymaster@4.0"
+        "android.hardware.keymaster@4.0",
     ],
 
     vintf_fragments: ["4.0/android.hardware.keymaster@4.0-service.trusty.xml"],
@@ -208,3 +208,36 @@
         "-Werror",
     ],
 }
+
+prebuilt_etc {
+    name: "rkp_uds_cert_test.xml",
+    vendor: true,
+    src: "set_uds_certs/rkp_uds_cert_test.xml",
+}
+
+cc_binary {
+    name: "trusty_rkp_set_uds_cert",
+    vendor: true,
+
+    srcs: [
+        "set_uds_certs/set_uds_certificates.cpp",
+        "ipc/trusty_keymaster_ipc.cpp",
+    ],
+
+    local_include_dirs: ["include"],
+
+    shared_libs: [
+        "libc",
+        "libcrypto",
+        "liblog",
+        "libtrusty",
+        "libhardware",
+        "libkeymaster_messages",
+        "libutils",
+        "libxml2",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index 09f696b..822e933 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -78,6 +78,8 @@
     KM_SET_ATTESTATION_IDS = (0xc000 << KEYMASTER_REQ_SHIFT),
     KM_SET_ATTESTATION_IDS_KM3 = (0xc001 << KEYMASTER_REQ_SHIFT),
     KM_CONFIGURE_BOOT_PATCHLEVEL = (0xd000 << KEYMASTER_REQ_SHIFT),
+    KM_APPEND_UDS_CERT_CHAIN = (0xe0000 << KEYMASTER_REQ_SHIFT),
+    KM_CLEAR_UDS_CERT_CHAIN = (0xe0001 << KEYMASTER_REQ_SHIFT),
 };
 
 #ifdef __ANDROID__
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h
index efad254..15241f4 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h
@@ -26,6 +26,7 @@
 const uint32_t TRUSTY_KEYMASTER_SEND_BUF_SIZE =
         (4096 - sizeof(struct keymaster_message) - 16 /* tipc header */);
 
+void trusty_keymaster_set_dev_name(const char* device_name);
 int trusty_keymaster_connect(void);
 int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
                           uint32_t* out_size);
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
index db1a9f4..c01877f 100644
--- a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -36,15 +36,18 @@
 #include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
 #include <utils/Timers.h>
 
-#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+static const char* trusty_device_name = "/dev/trusty-ipc-dev0";
 
 static int handle_ = -1;
 
 static const int timeout_ms = 10 * 1000;
 static const int max_timeout_ms = 60 * 1000;
 
+void trusty_keymaster_set_dev_name(const char* device_name) {
+    trusty_device_name = device_name;
+}
 int trusty_keymaster_connect() {
-    int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
+    int rc = tipc_connect(trusty_device_name, KEYMASTER_PORT);
     if (rc < 0) {
         return rc;
     }
diff --git a/trusty/keymaster/keymint/service.cpp b/trusty/keymaster/keymint/service.cpp
index 14549d2..583d840 100644
--- a/trusty/keymaster/keymint/service.cpp
+++ b/trusty/keymaster/keymint/service.cpp
@@ -18,11 +18,13 @@
 #include <android-base/logging.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
+#include <getopt.h>
 
 #include <trusty_keymaster/TrustyKeyMintDevice.h>
 #include <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>
 #include <trusty_keymaster/TrustySecureClock.h>
 #include <trusty_keymaster/TrustySharedSecret.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
 
 using aidl::android::hardware::security::keymint::trusty::TrustyKeyMintDevice;
 using aidl::android::hardware::security::keymint::trusty::TrustyRemotelyProvisionedComponentDevice;
@@ -39,7 +41,58 @@
     return service;
 }
 
-int main() {
+static const char* _sopts = "hD:";
+static const struct option _lopts[] = {
+        {"help", no_argument, 0, 'h'},
+        {"dev", required_argument, 0, 'D'},
+        {0, 0, 0, 0},
+};
+
+static const char* usage =
+        "Usage: %s [options]\n"
+        "\n"
+        "options:\n"
+        "  -h, --help            prints this message and exit\n"
+        "  -D, --dev name        Trusty device name\n"
+        "\n";
+
+static const char* usage_long = "\n";
+
+static void print_usage_and_exit(const char* prog, int code, bool verbose) {
+    fprintf(stderr, usage, prog);
+    if (verbose) {
+        fprintf(stderr, "%s", usage_long);
+    }
+    exit(code);
+}
+
+static void parse_options(int argc, char** argv) {
+    int c;
+    int oidx = 0;
+
+    while (1) {
+        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
+        if (c == -1) {
+            break; /* done */
+        }
+
+        switch (c) {
+            case 'D':
+                trusty_keymaster_set_dev_name(optarg);
+                break;
+
+            case 'h':
+                print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
+                break;
+
+            default:
+                print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+        }
+    }
+}
+
+int main(int argc, char** argv) {
+    parse_options(argc, argv);
     auto trustyKeymaster = std::make_shared<keymaster::TrustyKeymaster>();
     int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMINT_3);
     if (err != 0) {
diff --git a/trusty/keymaster/set_uds_certs/rkp_uds_cert_test.xml b/trusty/keymaster/set_uds_certs/rkp_uds_cert_test.xml
new file mode 100644
index 0000000..73b7b4c
--- /dev/null
+++ b/trusty/keymaster/set_uds_certs/rkp_uds_cert_test.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<PixelUdsCertificates>
+  <CertificateChain>
+    <NumberOfCertificates>3</NumberOfCertificates>
+    <Certificate format="pem">
+-----BEGIN CERTIFICATE-----
+MIIBWTCB3qADAgECAgUA3q2+7zAMBggqhkjOPQQDAwUAMBIxEDAOBgNVBAMMB0dT
+TUkxLjAwIhgPMjAyNDAxMDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowEjEQMA4G
+A1UEAwwHR1NNSTEuMDB2MBAGByqGSM49AgEGBSuBBAAiA2IABFsNPdsPmx2NKNiT
+7oReF36HTXjftRfGffYgPf/vEhrT7XLJ7dThkcb+OFYWYlOckdk3DRgqTNWQXsot
+UsZhwRveU8huEjOBO5+dwDiWPs+s9jSRAn5inlTqJ0YGAmdHRzAMBggqhkjOPQQD
+AwUAA2gAMGUCMQCuiJwmRWOgfWbqdSlnXfhCbphjdWc6sHelLkkM21vxQ3RZkhC2
+ERh90RA1rxB+XTgCMHZrYG3leS0PEoz5hUviv27LbBHBU6GeOzrS2e0VH0BMSNXN
+iP9Wnit+mJw58niEGw==
+-----END CERTIFICATE-----
+    </Certificate>
+    <Certificate format="pem">
+-----BEGIN CERTIFICATE-----
+MIIBWTCB3qADAgECAgUA3q2+7zAMBggqhkjOPQQDAwUAMBIxEDAOBgNVBAMMB0dT
+TUkxLjAwIhgPMjAyNDAxMDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowEjEQMA4G
+A1UEAwwHR1NNSTEuMDB2MBAGByqGSM49AgEGBSuBBAAiA2IABFsNPdsPmx2NKNiT
+7oReF36HTXjftRfGffYgPf/vEhrT7XLJ7dThkcb+OFYWYlOckdk3DRgqTNWQXsot
+UsZhwRveU8huEjOBO5+dwDiWPs+s9jSRAn5inlTqJ0YGAmdHRzAMBggqhkjOPQQD
+AwUAA2gAMGUCMQCuiJwmRWOgfWbqdSlnXfhCbphjdWc6sHelLkkM21vxQ3RZkhC2
+ERh90RA1rxB+XTgCMHZrYG3leS0PEoz5hUviv27LbBHBU6GeOzrS2e0VH0BMSNXN
+iP9Wnit+mJw58niEGw==
+-----END CERTIFICATE-----
+    </Certificate>
+    <Certificate format="pem">
+-----BEGIN CERTIFICATE-----
+MIIBWTCB3qADAgECAgUA3q2+7zAMBggqhkjOPQQDAwUAMBIxEDAOBgNVBAMMB0dT
+TUkxLjAwIhgPMjAyNDAxMDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowEjEQMA4G
+A1UEAwwHR1NNSTEuMDB2MBAGByqGSM49AgEGBSuBBAAiA2IABFsNPdsPmx2NKNiT
+7oReF36HTXjftRfGffYgPf/vEhrT7XLJ7dThkcb+OFYWYlOckdk3DRgqTNWQXsot
+UsZhwRveU8huEjOBO5+dwDiWPs+s9jSRAn5inlTqJ0YGAmdHRzAMBggqhkjOPQQD
+AwUAA2gAMGUCMQCuiJwmRWOgfWbqdSlnXfhCbphjdWc6sHelLkkM21vxQ3RZkhC2
+ERh90RA1rxB+XTgCMHZrYG3leS0PEoz5hUviv27LbBHBU6GeOzrS2e0VH0BMSNXN
+iP9Wnit+mJw58niEGw==
+-----END CERTIFICATE-----
+    </Certificate>
+  </CertificateChain>
+</PixelUdsCertificates>
diff --git a/trusty/keymaster/set_uds_certs/set_uds_certificates.cpp b/trusty/keymaster/set_uds_certs/set_uds_certificates.cpp
new file mode 100644
index 0000000..13356a9
--- /dev/null
+++ b/trusty/keymaster/set_uds_certs/set_uds_certificates.cpp
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <libxml/xmlreader.h>
+#include <openssl/pem.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <string>
+
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+static const char* _sopts = "h";
+static const struct option _lopts[] = {
+        {"help", no_argument, 0, 'h'},
+        {0, 0, 0, 0},
+};
+
+static const char* usage =
+        "Usage: %s [options] xml-file\n"
+        "\n"
+        "options:\n"
+        "  -h, --help            prints this message and exit\n"
+        "\n";
+
+static void print_usage_and_exit(const char* prog, int code) {
+    fprintf(stderr, usage, prog);
+    exit(code);
+}
+
+static void parse_options(int argc, char** argv) {
+    int c;
+    int oidx = 0;
+
+    while (1) {
+        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
+        if (c == -1) {
+            break; /* done */
+        }
+
+        switch (c) {
+            case 'h':
+                print_usage_and_exit(argv[0], EXIT_SUCCESS);
+                break;
+
+            default:
+                print_usage_and_exit(argv[0], EXIT_FAILURE);
+        }
+    }
+}
+
+struct AppendUdsCertificateRequest : public keymaster::KeymasterMessage {
+    explicit AppendUdsCertificateRequest(int32_t ver = keymaster::kDefaultMessageVersion)
+        : KeymasterMessage(ver) {}
+
+    size_t SerializedSize() const override { return cert_data.SerializedSize(); }
+    uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {
+        return cert_data.Serialize(buf, end);
+    }
+    bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {
+        return cert_data.Deserialize(buf_ptr, end);
+    }
+
+    keymaster::Buffer cert_data;
+};
+
+struct ClearUdsCertificateRequest : public keymaster::KeymasterMessage {
+    explicit ClearUdsCertificateRequest(int32_t ver = keymaster::kDefaultMessageVersion)
+        : KeymasterMessage(ver) {}
+
+    size_t SerializedSize() const override { return 0; }
+    uint8_t* Serialize(uint8_t* buf, const uint8_t*) const override { return buf; }
+    bool Deserialize(const uint8_t**, const uint8_t*) override { return true; };
+};
+
+struct KeymasterNoResponse : public keymaster::KeymasterResponse{
+    explicit KeymasterNoResponse(int32_t ver = keymaster::kDefaultMessageVersion)
+        : keymaster::KeymasterResponse(ver) {}
+
+    size_t NonErrorSerializedSize() const override { return 0; }
+    uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t*) const override { return buf; }
+    bool NonErrorDeserialize(const uint8_t**, const uint8_t*) override { return true; }
+};
+
+struct AppendUdsCertificateResponse : public KeymasterNoResponse {};
+struct ClearUdsCertificateResponse : public KeymasterNoResponse {};
+
+static int set_uds_cert_bin(uint32_t cmd, const void* cert_data, size_t cert_data_size) {
+    int ret;
+
+    AppendUdsCertificateRequest req;
+    req.cert_data.Reinitialize(cert_data, cert_data_size);
+    AppendUdsCertificateResponse rsp;
+
+    ret = trusty_keymaster_send(cmd, req, &rsp);
+    if (ret) {
+        fprintf(stderr, "trusty_keymaster_send cmd 0x%x failed %d\n", cmd, ret);
+        return ret;
+    }
+
+    return 0;
+}
+
+static int set_uds_cert_pem(uint32_t cmd, const xmlChar* pemkey) {
+    int ret;
+    int sslret;
+
+    /* Convert from pem to binary */
+    BIO* bio = BIO_new_mem_buf(pemkey, xmlStrlen(pemkey));
+    if (!bio) {
+        fprintf(stderr, "BIO_new_mem_buf failed\n");
+        ERR_print_errors_fp(stderr);
+        return -1;
+    }
+
+    char* key_name;
+    char* key_header;
+    uint8_t* key;
+    long keylen;
+    sslret = PEM_read_bio(bio, &key_name, &key_header, &key, &keylen);
+    BIO_free(bio);
+
+    if (!sslret) {
+        fprintf(stderr, "PEM_read_bio failed\n");
+        ERR_print_errors_fp(stderr);
+        return -1;
+    }
+
+    /* Send key in binary format to trusty */
+    ret = set_uds_cert_bin(cmd, key, keylen);
+
+    OPENSSL_free(key_name);
+    OPENSSL_free(key_header);
+    OPENSSL_free(key);
+
+    return ret;
+}
+
+static int set_uds_cert(uint32_t cmd, const xmlChar* format, const xmlChar* str) {
+    int ret;
+
+    if (xmlStrEqual(format, BAD_CAST "pem")) {
+        ret = set_uds_cert_pem(cmd, str);
+    } else {
+        printf("unsupported key/cert format: %s\n", format);
+        return -1;
+    }
+    return ret;
+}
+
+// TODO: Guard by Production Mode
+static int clear_cert_chain() {
+    int ret;
+    ClearUdsCertificateRequest req;
+    ClearUdsCertificateResponse rsp;
+
+    ret = trusty_keymaster_send(KM_CLEAR_UDS_CERT_CHAIN, req, &rsp);
+    if (ret) {
+        fprintf(stderr, "%s: trusty_keymaster_send failed %d\n", __func__, ret);
+        return ret;
+    }
+    return 0;
+}
+
+static int process_xml(xmlTextReaderPtr xml) {
+    int ret;
+    const xmlChar* element = NULL;
+    const xmlChar* element_format = NULL;
+    bool isPixelUdsCert = false;
+
+    while ((ret = xmlTextReaderRead(xml)) == 1) {
+        int nodetype = xmlTextReaderNodeType(xml);
+        const xmlChar *name, *value;
+        name = xmlTextReaderConstName(xml);
+        switch (nodetype) {
+            case XML_READER_TYPE_ELEMENT:
+                element = name;
+                element_format = xmlTextReaderGetAttribute(xml, BAD_CAST "format");
+                if (isPixelUdsCert || xmlStrEqual(name, BAD_CAST "PixelUdsCertificates")) {
+                    // The first element name must be "PixelUdsCertificates"
+                    isPixelUdsCert = true;
+                } else {
+                    fprintf(stderr, "Not a PixelUdsCertificates: \"%s\"\n", name);
+                    return -1;
+                }
+                if (xmlStrEqual(name, BAD_CAST "CertificateChain")) {
+                    ret = clear_cert_chain();
+                    if (ret) {
+                        fprintf(stderr, "%s: Clear cert chain cmd failed, %d\n", element, ret);
+                        return ret;
+                    }
+                    printf("%s: Clear cert chain cmd done\n", element);
+                }
+                break;
+            case XML_READER_TYPE_TEXT:
+                value = xmlTextReaderConstValue(xml);
+                uint32_t cmd;
+                if (xmlStrEqual(element, BAD_CAST "Certificate")) {
+                    cmd = KM_APPEND_UDS_CERT_CHAIN;
+                } else {
+                    break;
+                }
+
+                ret = set_uds_cert(cmd, element_format, value);
+                if (ret) {
+                    fprintf(stderr, "%s, format %s: Cmd 0x%x failed, %d\n", element, element_format,
+                            cmd, ret);
+                    return ret;
+                }
+                printf("%s, format %s: Cmd 0x%x done\n", element, element_format, cmd);
+                break;
+            case XML_READER_TYPE_END_ELEMENT:
+                element = NULL;
+                break;
+        }
+    }
+    return ret;
+}
+
+static int parse_and_provision_xml_file(const char* filename) {
+    int ret;
+    xmlTextReaderPtr xml = xmlReaderForFile(filename, NULL, 0);
+    if (!xml) {
+        fprintf(stderr, "failed to open %s\n", filename);
+        return -1;
+    }
+
+    ret = process_xml(xml);
+
+    xmlFreeTextReader(xml);
+    if (ret != 0) {
+        fprintf(stderr, "Failed to parse or process %s\n", filename);
+        return -1;
+    }
+
+    return 0;
+}
+
+int main(int argc, char** argv) {
+    int ret = 0;
+
+    parse_options(argc, argv);
+    if (optind + 1 != argc) {
+        print_usage_and_exit(argv[0], EXIT_FAILURE);
+    }
+
+    ret = trusty_keymaster_connect();
+    if (ret) {
+        fprintf(stderr, "trusty_keymaster_connect failed %d\n", ret);
+        return EXIT_FAILURE;
+    }
+
+    ret = parse_and_provision_xml_file(argv[optind]);
+    if (ret) {
+        fprintf(stderr, "parse_and_provision_xml_file failed %d\n", ret);
+        trusty_keymaster_disconnect();
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
diff --git a/trusty/keymint/Android.bp b/trusty/keymint/Android.bp
index 19dcc98..92d9c6f 100644
--- a/trusty/keymint/Android.bp
+++ b/trusty/keymint/Android.bp
@@ -24,11 +24,12 @@
     init_rc: ["android.hardware.security.keymint-service.rust.trusty.rc"],
     vintf_fragments: ["android.hardware.security.keymint-service.rust.trusty.xml"],
     srcs: [
-        "src/keymint_hal_main.rs"
+        "src/keymint_hal_main.rs",
     ],
     rustlibs: [
         "libandroid_logger",
         "libbinder_rs",
+        "libclap",
         "libkmr_wire",
         "libkmr_hal",
         "libtrusty-rs",
diff --git a/trusty/keymint/src/keymint_hal_main.rs b/trusty/keymint/src/keymint_hal_main.rs
index ef0c598..3c5627b 100644
--- a/trusty/keymint/src/keymint_hal_main.rs
+++ b/trusty/keymint/src/keymint_hal_main.rs
@@ -14,6 +14,7 @@
 // limitations under the License.
 
 //! This module implements the HAL service for Keymint (Rust) in Trusty.
+use clap::Parser;
 use kmr_hal::{
     extract_rsp, keymint, rpc, secureclock, send_hal_info, sharedsecret, SerializedChannel,
 };
@@ -81,6 +82,13 @@
     }
 }
 
+#[derive(Parser, Debug)]
+struct Args {
+    /// Tipc device path
+    #[arg(short, long, default_value_t = DEFAULT_DEVICE.to_string())]
+    dev: String,
+}
+
 fn main() {
     if let Err(HalServiceError(e)) = inner_main() {
         panic!("HAL service failed: {:?}", e);
@@ -88,6 +96,7 @@
 }
 
 fn inner_main() -> Result<(), HalServiceError> {
+    let args = Args::parse();
     // Initialize Android logging.
     android_logger::init_once(
         android_logger::Config::default()
@@ -106,10 +115,15 @@
     binder::ProcessState::start_thread_pool();
 
     // Create connection to the TA
-    let connection = trusty::TipcChannel::connect(DEFAULT_DEVICE, TRUSTY_KEYMINT_RUST_SERVICE_NAME)
-        .map_err(|e| {
-            HalServiceError(format!("Failed to connect to Trusty Keymint TA because of {:?}.", e))
-        })?;
+    let connection =
+        trusty::TipcChannel::connect(args.dev.as_str(), TRUSTY_KEYMINT_RUST_SERVICE_NAME).map_err(
+            |e| {
+                HalServiceError(format!(
+                    "Failed to connect to Trusty Keymint TA at {} because of {:?}.",
+                    args.dev, e
+                ))
+            },
+        )?;
     let tipc_channel = Arc::new(Mutex::new(TipcChannel(connection)));
 
     // Register the Keymint service
diff --git a/trusty/libtrusty/trusty.c b/trusty/libtrusty/trusty.c
index f44f8b4..63262a0 100644
--- a/trusty/libtrusty/trusty.c
+++ b/trusty/libtrusty/trusty.c
@@ -23,16 +23,161 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
 #include <unistd.h>
 
+#include <linux/vm_sockets.h> /* must be after sys/socket.h */
 #include <log/log.h>
 
 #include <trusty/ipc.h>
 
+static const char* strip_prefix(const char* str, const char* prefix) {
+    size_t prefix_len = strlen(prefix);
+    if (strncmp(str, prefix, prefix_len) == 0) {
+        return str + prefix_len;
+    } else {
+        return NULL;
+    }
+}
+
+static bool use_vsock_connection = false;
+static int tipc_vsock_connect(const char* type_cid_port_str, const char* srv_name) {
+    int ret;
+    const char* cid_port_str;
+    char* port_str;
+    char* end_str;
+    int socket_type;
+    if ((cid_port_str = strip_prefix(type_cid_port_str, "STREAM:"))) {
+        socket_type = SOCK_STREAM;
+    } else if ((cid_port_str = strip_prefix(type_cid_port_str, "SEQPACKET:"))) {
+        socket_type = SOCK_SEQPACKET;
+    } else {
+        /*
+         * Default to SOCK_STREAM if neither type is specified.
+         *
+         * TODO: use SOCK_SEQPACKET by default instead of SOCK_STREAM when SOCK_SEQPACKET is fully
+         * supported since it matches tipc better. At the moment SOCK_SEQPACKET is not supported by
+         * crosvm. It is also significantly slower since the Linux kernel implementation (as of
+         * v6.7-rc1) sends credit update packets every time it receives a data packet while the
+         * SOCK_STREAM version skips these unless the remaining buffer space is "low".
+         */
+        socket_type = SOCK_STREAM;
+        cid_port_str = type_cid_port_str;
+    }
+    long cid = strtol(cid_port_str, &port_str, 0);
+    if (port_str[0] != ':') {
+        ALOGE("%s: invalid VSOCK str, \"%s\", need cid:port missing : after cid\n", __func__,
+              cid_port_str);
+        return -EINVAL;
+    }
+    long port = strtol(port_str + 1, &end_str, 0);
+    if (end_str[0] != '\0') {
+        ALOGE("%s: invalid VSOCK str, \"%s\", need cid:port got %ld:%ld\n", __func__, cid_port_str,
+              cid, port);
+        return -EINVAL;
+    }
+    int fd = socket(AF_VSOCK, socket_type, 0);
+    if (fd < 0) {
+        ret = -errno;
+        ALOGE("%s: can't get vsock %ld:%ld socket for tipc service \"%s\" (err=%d)\n", __func__,
+              cid, port, srv_name, errno);
+        return ret < 0 ? ret : -1;
+    }
+    struct timeval connect_timeout = {.tv_sec = 60, .tv_usec = 0};
+    ret = setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_CONNECT_TIMEOUT, &connect_timeout,
+                     sizeof(connect_timeout));
+    if (ret) {
+        ALOGE("%s: vsock %ld:%ld: Failed to set connect timeout (err=%d)\n", __func__, cid, port,
+              errno);
+        /* failed to set longer timeout, but try to connect anyway */
+    }
+    struct sockaddr_vm sa = {
+            .svm_family = AF_VSOCK,
+            .svm_port = port,
+            .svm_cid = cid,
+    };
+    int retry = 10;
+    do {
+        ret = TEMP_FAILURE_RETRY(connect(fd, (struct sockaddr*)&sa, sizeof(sa)));
+        if (ret && (errno == ENODEV || errno == ESOCKTNOSUPPORT) && --retry) {
+            /*
+             * The kernel returns ESOCKTNOSUPPORT instead of ENODEV if the socket type is
+             * SOCK_SEQPACKET and the guest CID we are trying to connect to is not ready yet.
+             */
+            ALOGE("%s: Can't connect to vsock %ld:%ld for tipc service \"%s\" (err=%d) %d retries "
+                  "remaining\n",
+                  __func__, cid, port, srv_name, errno, retry);
+            sleep(1);
+        } else {
+            retry = 0;
+        }
+    } while (retry);
+    if (ret) {
+        ret = -errno;
+        ALOGE("%s: Can't connect to vsock %ld:%ld for tipc service \"%s\" (err=%d)\n", __func__,
+              cid, port, srv_name, errno);
+        close(fd);
+        return ret < 0 ? ret : -1;
+    }
+    /*
+     * TODO: Current vsock tipc bridge in trusty expects a port name in the
+     * first packet. We need to replace this with a protocol that also does DICE
+     * based authentication.
+     */
+    ret = TEMP_FAILURE_RETRY(write(fd, srv_name, strlen(srv_name)));
+    if (ret != strlen(srv_name)) {
+        ret = -errno;
+        ALOGE("%s: vsock %ld:%ld: failed to send tipc service name \"%s\" (err=%d)\n", __func__,
+              cid, port, srv_name, errno);
+        close(fd);
+        return ret < 0 ? ret : -1;
+    }
+    /*
+     * Work around lack of seq packet support. Read a status byte to prevent
+     * the caller from sending more data until srv_name has been read.
+     */
+    int8_t status;
+    ret = TEMP_FAILURE_RETRY(read(fd, &status, sizeof(status)));
+    if (ret != sizeof(status)) {
+        ALOGE("%s: vsock %ld:%ld: failed to read status byte for connect to tipc service name "
+              "\"%s\" (err=%d)\n",
+              __func__, cid, port, srv_name, errno);
+        close(fd);
+        return ret < 0 ? ret : -1;
+    }
+    use_vsock_connection = true;
+    return fd;
+}
+
+static size_t tipc_vsock_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shms,
+                              int shmcnt) {
+    int ret;
+
+    (void)shms;
+    if (shmcnt != 0) {
+        ALOGE("%s: vsock does not yet support passing fds\n", __func__);
+        return -ENOTSUP;
+    }
+    ret = TEMP_FAILURE_RETRY(writev(fd, iov, iovcnt));
+    if (ret < 0) {
+        ret = -errno;
+        ALOGE("%s: failed to send message (err=%d)\n", __func__, errno);
+        return ret < 0 ? ret : -1;
+    }
+
+    return ret;
+}
+
 int tipc_connect(const char* dev_name, const char* srv_name) {
     int fd;
     int rc;
 
+    const char* type_cid_port_str = strip_prefix(dev_name, "VSOCK:");
+    if (type_cid_port_str) {
+        return tipc_vsock_connect(type_cid_port_str, srv_name);
+    }
+
     fd = TEMP_FAILURE_RETRY(open(dev_name, O_RDWR));
     if (fd < 0) {
         rc = -errno;
@@ -54,6 +199,9 @@
 
 ssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shms,
                   int shmcnt) {
+    if (use_vsock_connection) {
+        return tipc_vsock_send(fd, iov, iovcnt, shms, shmcnt);
+    }
     struct tipc_send_msg_req req;
     req.iov = (__u64)iov;
     req.iov_cnt = (__u64)iovcnt;
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index 6d0c616..ca39f6a 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -55,7 +55,7 @@
 static struct storage_mapping_node* storage_mapping_head;
 
 /*
- * Property set to 1 after we have opened a file under ssdir_name. The backing
+ * Properties set to 1 after we have opened a file under ssdir_name. The backing
  * files for both TD and TDP are currently located under /data/vendor/ss and can
  * only be opened once userdata is mounted. This storageproxyd service is
  * restarted when userdata is available, which causes the Trusty storage service
@@ -64,11 +64,16 @@
  * ports will be available (although they may block if still being initialized),
  * and connections will not be reset after this point (assuming the
  * storageproxyd service stays running).
+ *
+ * fs_ready - secure storage is read-only (due to checkpointing after upgrade)
+ * fs_ready_rw - secure storage is readable and writable
  */
 #define FS_READY_PROPERTY "ro.vendor.trusty.storage.fs_ready"
+#define FS_READY_RW_PROPERTY "ro.vendor.trusty.storage.fs_ready_rw"
 
 /* has FS_READY_PROPERTY been set? */
-static bool fs_ready_initialized = false;
+static bool fs_ready_set = false;
+static bool fs_ready_rw_set = false;
 
 static enum sync_state fs_state;
 static enum sync_state fd_state[FD_TBL_SIZE];
@@ -80,6 +85,17 @@
    uint8_t data[MAX_READ_SIZE];
 }  read_rsp;
 
+static bool property_set_helper(const char* prop) {
+    int rc = property_set(prop, "1");
+    if (rc == 0) {
+        ALOGI("Set property %s\n", prop);
+    } else {
+        ALOGE("Could not set property %s, rc: %d\n", prop, rc);
+    }
+
+    return rc == 0;
+}
+
 static uint32_t insert_fd(int open_flags, int fd, struct storage_mapping_node* node) {
     uint32_t handle = fd;
 
@@ -520,12 +536,20 @@
     path = NULL;
 
     /* a backing file has been opened, notify any waiting init steps */
-    if (!fs_ready_initialized) {
-        rc = property_set(FS_READY_PROPERTY, "1");
-        if (rc == 0) {
-            fs_ready_initialized = true;
+    if (!fs_ready_set || !fs_ready_rw_set) {
+        bool is_checkpoint_active = false;
+
+        rc = is_data_checkpoint_active(&is_checkpoint_active);
+        if (rc != 0) {
+            ALOGE("is_data_checkpoint_active() failed (%d)\n", rc);
         } else {
-            ALOGE("Could not set property %s, rc: %d\n", FS_READY_PROPERTY, rc);
+            if (!fs_ready_rw_set && !is_checkpoint_active) {
+                fs_ready_rw_set = property_set_helper(FS_READY_RW_PROPERTY);
+            }
+
+            if (!fs_ready_set) {
+                fs_ready_set = property_set_helper(FS_READY_PROPERTY);
+            }
         }
     }