Merge "Add mock interface for EmitXorBlocks"
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index d7e8f32..f713ff2 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -13,6 +13,7 @@
         "-Wno-unused-function",
         "-Wno-nullability-completeness",
         "-Os",
+        "-fno-finite-loops",
     ],
 
     local_include_dirs: ["include"],
diff --git a/debuggerd/OWNERS b/debuggerd/OWNERS
index bfeedca..6f7e4a3 100644
--- a/debuggerd/OWNERS
+++ b/debuggerd/OWNERS
@@ -1,2 +1 @@
 cferris@google.com
-jmgao@google.com
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 360ea95..e20e8d9 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -93,8 +93,18 @@
     errx(1, "process %d is a zombie", pid);
   }
 
-  if (kill(pid, 0) != 0) {
-    err(1, "cannot send signal to process %d", pid);
+  // Send a signal to the main thread pid, not a side thread. The signal
+  // handler always sets the crashing tid to the main thread pid when sent this
+  // signal. This is to avoid a problem where the signal is sent to a process,
+  // but happens on a side thread and the intercept mismatches since it
+  // is looking for the main thread pid, not the tid of this random thread.
+  // See b/194346289 for extra details.
+  if (kill(proc_info.pid, 0) != 0) {
+    if (pid == proc_info.pid) {
+      err(1, "cannot send signal to process %d", pid);
+    } else {
+      err(1, "cannot send signal to main thread %d (requested thread %d)", proc_info.pid, pid);
+    }
   }
 
   unique_fd piperead, pipewrite;
@@ -103,9 +113,13 @@
   }
 
   std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
-  if (!debuggerd_trigger_dump(pid, dump_type, 0, std::move(pipewrite))) {
+  if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(pipewrite))) {
     redirect_thread.join();
-    errx(1, "failed to dump process %d", pid);
+    if (pid == proc_info.pid) {
+      errx(1, "failed to dump process %d", pid);
+    } else {
+      errx(1, "failed to dump main thread %d (requested thread %d)", proc_info.pid, pid);
+    }
   }
 
   redirect_thread.join();
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index abda071..f24c4fc 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -1825,3 +1825,29 @@
   ASSERT_TRUE(android::base::ReadFdToString(output_fd, &output));
   ASSERT_EQ("foo", output);
 }
+
+// Verify that when an intercept is present for the main thread, and the signal
+// is received on a different thread, the intercept still works.
+TEST_F(CrasherTest, intercept_for_main_thread_signal_on_side_thread) {
+  StartProcess([]() {
+    std::thread thread([]() {
+      // Raise the signal on the side thread.
+      raise_debugger_signal(kDebuggerdNativeBacktrace);
+    });
+    thread.join();
+    _exit(0);
+  });
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd, kDebuggerdNativeBacktrace);
+  FinishCrasher();
+  AssertDeath(0);
+
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 375dbed..35be2bf 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -155,18 +155,14 @@
  * could allocate memory or hold a lock.
  */
 static void log_signal_summary(const siginfo_t* info) {
-  char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination
-  if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
-    strcpy(thread_name, "<name unknown>");
-  } else {
-    // short names are null terminated by prctl, but the man page
-    // implies that 16 byte names are not.
-    thread_name[MAX_TASK_NAME_LEN] = 0;
+  char main_thread_name[MAX_TASK_NAME_LEN + 1];
+  if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
+    strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
   }
 
   if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {
-    async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
-                          thread_name);
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for pid %d (%s)", __getpid(),
+                          main_thread_name);
     return;
   }
 
@@ -181,9 +177,13 @@
     get_signal_sender(sender_desc, sizeof(sender_desc), info);
   }
 
-  char main_thread_name[MAX_TASK_NAME_LEN + 1];
-  if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
-    strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
+  char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination
+  if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
+    strcpy(thread_name, "<name unknown>");
+  } else {
+    // short names are null terminated by prctl, but the man page
+    // implies that 16 byte names are not.
+    thread_name[MAX_TASK_NAME_LEN] = 0;
   }
 
   async_safe_format_log(ANDROID_LOG_FATAL, "libc",
@@ -532,8 +532,13 @@
 
   log_signal_summary(info);
 
+  // If we got here due to the signal BIONIC_SIGNAL_DEBUGGER, it's possible
+  // this is not the main thread, which can cause the intercept logic to fail
+  // since the intercept is only looking for the main thread. In this case,
+  // setting crashing_tid to pid instead of the current thread's tid avoids
+  // the problem.
   debugger_thread_info thread_info = {
-      .crashing_tid = __gettid(),
+      .crashing_tid = (signal_number == BIONIC_SIGNAL_DEBUGGER) ? __getpid() : __gettid(),
       .pseudothread_tid = -1,
       .siginfo = info,
       .ucontext = context,
diff --git a/diagnose_usb/OWNERS b/diagnose_usb/OWNERS
index 643b448..fcd7757 100644
--- a/diagnose_usb/OWNERS
+++ b/diagnose_usb/OWNERS
@@ -1,2 +1 @@
-jmgao@google.com
 yabinc@google.com
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index a72ee07..58b2a81 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,4 +1,3 @@
 dvander@google.com
 hridya@google.com
 enh@google.com
-jmgao@google.com
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index ee0aa58..9b5d2cd 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -187,11 +187,17 @@
                                  ", build may be missing broken or missing boot_devices");
     }
 
+    std::string slot_suffix = device->GetCurrentSlot();
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+
+    std::string other_slot_suffix;
+    if (!slot_suffix.empty()) {
+        other_slot_suffix = (slot_suffix == "_a") ? "_b" : "_a";
+    }
+
     // If we are unable to read the existing metadata, then the super partition
     // is corrupt. In this case we reflash the whole thing using the provided
     // image.
-    std::string slot_suffix = device->GetCurrentSlot();
-    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
     std::unique_ptr<LpMetadata> old_metadata = ReadMetadata(super_name, slot_number);
     if (wipe || !old_metadata) {
         if (!FlashPartitionTable(super_name, *new_metadata.get())) {
@@ -203,11 +209,15 @@
     }
 
     std::set<std::string> partitions_to_keep;
+    bool virtual_ab = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
     for (const auto& partition : old_metadata->partitions) {
         // Preserve partitions in the other slot, but not the current slot.
         std::string partition_name = GetPartitionName(partition);
-        if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) {
-            continue;
+        if (!slot_suffix.empty()) {
+            auto part_suffix = GetPartitionSlotSuffix(partition_name);
+            if (part_suffix == slot_suffix || (part_suffix == other_slot_suffix && virtual_ab)) {
+                continue;
+            }
         }
         std::string group_name = GetPartitionGroupName(old_metadata->groups[partition.group_index]);
         // Skip partitions in the COW group
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index c87ce60..532b524 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -941,7 +941,8 @@
         // Tries to locate top-level vbmeta from boot.img footer.
         uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
         if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
-            die("Failed to find AVB_FOOTER at offset: %" PRId64, footer_offset);
+            die("Failed to find AVB_FOOTER at offset: %" PRId64 ", is BOARD_AVB_ENABLE true?",
+                footer_offset);
         }
         const AvbFooter* footer = reinterpret_cast<const AvbFooter*>(data.c_str() + footer_offset);
         vbmeta_offset = be64toh(footer->vbmeta_offset);
@@ -1021,6 +1022,24 @@
         return;
     }
 
+    // If overflows and negative, it should be < buf->sz.
+    int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
+
+    if (partition_size == buf->sz) {
+        return;
+    }
+    // Some device bootloaders might not implement `fastboot getvar partition-size:boot[_a|_b]`.
+    // In this case, partition_size will be zero.
+    if (partition_size < buf->sz) {
+        fprintf(stderr,
+                "Warning: skip copying boot image avb footer"
+                " (boot partition size: %" PRId64 ", boot image size: %" PRId64 ").\n",
+                partition_size, buf->sz);
+        return;
+    }
+
+    // IMPORTANT: after the following read, we need to reset buf->fd before return (if not die).
+    // Because buf->fd will still be used afterwards.
     std::string data;
     if (!android::base::ReadFdToString(buf->fd, &data)) {
         die("Failed reading from boot");
@@ -1028,17 +1047,9 @@
 
     uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
     if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
+        lseek(buf->fd.get(), 0, SEEK_SET);  // IMPORTANT: resets buf->fd before return.
         return;
     }
-    // If overflows and negative, it should be < buf->sz.
-    int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
-
-    if (partition_size == buf->sz) {
-        return;
-    }
-    if (partition_size < buf->sz) {
-        die("boot partition is smaller than boot image");
-    }
 
     unique_fd fd(make_temporary_fd("boot rewriting"));
     if (!android::base::WriteStringToFd(data, fd)) {
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index abd2483..6170aee 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -65,6 +65,7 @@
         "-D_FILE_OFFSET_BITS=64",
     ],
     srcs: [
+        "blockdev.cpp",
         "file_wait.cpp",
         "fs_mgr.cpp",
         "fs_mgr_format.cpp",
@@ -144,6 +145,7 @@
     // It does not have a stable interface.
     name: "libfs_mgr",
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     defaults: [
         "libfs_mgr_defaults",
@@ -169,6 +171,7 @@
     name: "libfstab",
     vendor_available: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     host_supported: true,
     defaults: ["fs_mgr_defaults"],
diff --git a/fs_mgr/blockdev.cpp b/fs_mgr/blockdev.cpp
new file mode 100644
index 0000000..14b217c
--- /dev/null
+++ b/fs_mgr/blockdev.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2021 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 <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <dirent.h>
+#include <libdm/dm.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include "blockdev.h"
+
+using android::base::Basename;
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::ResultError;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::dm::DeviceMapper;
+
+// Return the parent device of a partition. Converts e.g. "sda26" into "sda".
+static std::string PartitionParent(const std::string& blockdev) {
+    if (blockdev.find('/') != std::string::npos) {
+        LOG(ERROR) << __func__ << ": invalid argument " << blockdev;
+        return blockdev;
+    }
+    auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/sys/class/block"), closedir};
+    if (!dir) {
+        return blockdev;
+    }
+    for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
+        if (ent->d_name[0] == '.') {
+            continue;
+        }
+        std::string path = StringPrintf("/sys/class/block/%s/%s", ent->d_name, blockdev.c_str());
+        struct stat statbuf;
+        if (stat(path.c_str(), &statbuf) >= 0) {
+            return ent->d_name;
+        }
+    }
+    return blockdev;
+}
+
+// Convert a major:minor pair into a block device name.
+static std::string BlockdevName(dev_t dev) {
+    auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/dev/block"), closedir};
+    if (!dir) {
+        return {};
+    }
+    for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
+        if (ent->d_name[0] == '.') {
+            continue;
+        }
+        const std::string path = std::string("/dev/block/") + ent->d_name;
+        struct stat statbuf;
+        if (stat(path.c_str(), &statbuf) >= 0 && dev == statbuf.st_rdev) {
+            return ent->d_name;
+        }
+    }
+    return {};
+}
+
+// Trim whitespace from the end of a string.
+static void rtrim(std::string& s) {
+    s.erase(s.find_last_not_of('\n') + 1, s.length());
+}
+
+// For file `file_path`, retrieve the block device backing the filesystem on
+// which the file exists and return the queue depth of the block device.
+static Result<uint32_t> BlockDeviceQueueDepth(const std::string& file_path) {
+    struct stat statbuf;
+    int res = stat(file_path.c_str(), &statbuf);
+    if (res < 0) {
+        return ErrnoError() << "stat(" << file_path << ")";
+    }
+    std::string blockdev = "/dev/block/" + BlockdevName(statbuf.st_dev);
+    LOG(DEBUG) << __func__ << ": " << file_path << " -> " << blockdev;
+    if (blockdev.empty()) {
+        const std::string err_msg =
+                StringPrintf("Failed to convert %u:%u (path %s)", major(statbuf.st_dev),
+                             minor(statbuf.st_dev), file_path.c_str());
+        return ResultError(err_msg, 0);
+    }
+    auto& dm = DeviceMapper::Instance();
+    for (;;) {
+        std::optional<std::string> child = dm.GetParentBlockDeviceByPath(blockdev);
+        if (!child) {
+            break;
+        }
+        LOG(DEBUG) << __func__ << ": " << blockdev << " -> " << *child;
+        blockdev = *child;
+    }
+    std::optional<std::string> maybe_blockdev = android::dm::ExtractBlockDeviceName(blockdev);
+    if (!maybe_blockdev) {
+        return ResultError("Failed to remove /dev/block/ prefix from " + blockdev, 0);
+    }
+    blockdev = PartitionParent(*maybe_blockdev);
+    LOG(DEBUG) << __func__ << ": "
+               << "Partition parent: " << blockdev;
+    const std::string nr_tags_path =
+            StringPrintf("/sys/class/block/%s/mq/0/nr_tags", blockdev.c_str());
+    std::string nr_tags;
+    if (!android::base::ReadFileToString(nr_tags_path, &nr_tags)) {
+        return ResultError("Failed to read " + nr_tags_path, 0);
+    }
+    rtrim(nr_tags);
+    LOG(DEBUG) << __func__ << ": " << file_path << " is backed by /dev/" << blockdev
+               << " and that block device supports queue depth " << nr_tags;
+    return strtol(nr_tags.c_str(), NULL, 0);
+}
+
+// Set 'nr_requests' of `loop_device_path` to the queue depth of the block
+// device backing `file_path`.
+Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
+                                 const std::string& file_path) {
+    if (!StartsWith(loop_device_path, "/dev/")) {
+        return Error() << "Invalid argument " << loop_device_path;
+    }
+
+    const std::string loop_device_name = Basename(loop_device_path);
+
+    const Result<uint32_t> qd = BlockDeviceQueueDepth(file_path);
+    if (!qd.ok()) {
+        LOG(DEBUG) << __func__ << ": "
+                   << "BlockDeviceQueueDepth() returned " << qd.error();
+        return ResultError(qd.error());
+    }
+    const std::string nr_requests = StringPrintf("%u", *qd);
+    const std::string sysfs_path =
+            StringPrintf("/sys/class/block/%s/queue/nr_requests", loop_device_name.c_str());
+    unique_fd sysfs_fd(open(sysfs_path.c_str(), O_RDWR | O_CLOEXEC));
+    if (sysfs_fd == -1) {
+        return ErrnoError() << "Failed to open " << sysfs_path;
+    }
+
+    const int res = write(sysfs_fd.get(), nr_requests.data(), nr_requests.length());
+    if (res < 0) {
+        return ErrnoError() << "Failed to write to " << sysfs_path;
+    }
+    return {};
+}
diff --git a/fs_mgr/blockdev.h b/fs_mgr/blockdev.h
new file mode 100644
index 0000000..2c0d68a
--- /dev/null
+++ b/fs_mgr/blockdev.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 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 <android-base/result.h>
+#include <string>
+
+android::base::Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
+                                                const std::string& file_path);
diff --git a/fs_mgr/file_wait.cpp b/fs_mgr/file_wait.cpp
index cbf6845..af0699b 100644
--- a/fs_mgr/file_wait.cpp
+++ b/fs_mgr/file_wait.cpp
@@ -206,6 +206,9 @@
 }
 
 int64_t OneShotInotify::RemainingMs() const {
+    if (relative_timeout_ == std::chrono::milliseconds::max()) {
+        return std::chrono::milliseconds::max().count();
+    }
     auto remaining = (std::chrono::steady_clock::now() - start_time_);
     auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(remaining);
     return (relative_timeout_ - elapsed).count();
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 01c8ad3..5f15aad 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -34,11 +34,13 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <array>
 #include <chrono>
 #include <functional>
 #include <map>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <thread>
 #include <utility>
 #include <vector>
@@ -70,6 +72,7 @@
 #include <log/log_properties.h>
 #include <logwrap/logwrap.h>
 
+#include "blockdev.h"
 #include "fs_mgr_priv.h"
 
 #define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
@@ -101,6 +104,7 @@
 using android::base::Realpath;
 using android::base::SetProperty;
 using android::base::StartsWith;
+using android::base::StringPrintf;
 using android::base::Timer;
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
@@ -790,20 +794,26 @@
     int save_errno = 0;
     int gc_allowance = 0;
     std::string opts;
+    std::string checkpoint_opts;
     bool try_f2fs_gc_allowance = is_f2fs(entry.fs_type) && entry.fs_checkpoint_opts.length() > 0;
+    bool try_f2fs_fallback = false;
     Timer t;
 
     do {
-        if (save_errno == EINVAL && try_f2fs_gc_allowance) {
-            PINFO << "Kernel does not support checkpoint=disable:[n]%, trying without.";
+        if (save_errno == EINVAL && (try_f2fs_gc_allowance || try_f2fs_fallback)) {
+            PINFO << "Kernel does not support " << checkpoint_opts << ", trying without.";
             try_f2fs_gc_allowance = false;
+            // Attempt without gc allowance before dropping.
+            try_f2fs_fallback = !try_f2fs_fallback;
         }
         if (try_f2fs_gc_allowance) {
-            opts = entry.fs_options + entry.fs_checkpoint_opts + ":" +
-                   std::to_string(gc_allowance) + "%";
+            checkpoint_opts = entry.fs_checkpoint_opts + ":" + std::to_string(gc_allowance) + "%";
+        } else if (try_f2fs_fallback) {
+            checkpoint_opts = entry.fs_checkpoint_opts;
         } else {
-            opts = entry.fs_options;
+            checkpoint_opts = "";
         }
+        opts = entry.fs_options + checkpoint_opts;
         if (save_errno == EAGAIN) {
             PINFO << "Retrying mount (source=" << source << ",target=" << target
                   << ",type=" << entry.fs_type << ", gc_allowance=" << gc_allowance << "%)=" << ret
@@ -814,7 +824,7 @@
         save_errno = errno;
         if (try_f2fs_gc_allowance) gc_allowance += 10;
     } while ((ret && save_errno == EAGAIN && gc_allowance <= 100) ||
-             (ret && save_errno == EINVAL && try_f2fs_gc_allowance));
+             (ret && save_errno == EINVAL && (try_f2fs_gc_allowance || try_f2fs_fallback)));
     const char* target_missing = "";
     const char* source_missing = "";
     if (save_errno == ENOENT) {
@@ -2038,6 +2048,35 @@
     return 0;
 }
 
+static bool ConfigureIoScheduler(const std::string& device_path) {
+    if (!StartsWith(device_path, "/dev/")) {
+        LERROR << __func__ << ": invalid argument " << device_path;
+        return false;
+    }
+
+    const std::string iosched_path =
+            StringPrintf("/sys/block/%s/queue/scheduler", Basename(device_path).c_str());
+    unique_fd iosched_fd(open(iosched_path.c_str(), O_RDWR | O_CLOEXEC));
+    if (iosched_fd.get() == -1) {
+        PERROR << __func__ << ": failed to open " << iosched_path;
+        return false;
+    }
+
+    // Kernels before v4.1 only support 'noop'. Kernels [v4.1, v5.0) support
+    // 'noop' and 'none'. Kernels v5.0 and later only support 'none'.
+    static constexpr const std::array<std::string_view, 2> kNoScheduler = {"none", "noop"};
+
+    for (const std::string_view& scheduler : kNoScheduler) {
+        int ret = write(iosched_fd.get(), scheduler.data(), scheduler.size());
+        if (ret > 0) {
+            return true;
+        }
+    }
+
+    PERROR << __func__ << ": failed to write to " << iosched_path;
+    return false;
+}
+
 static bool InstallZramDevice(const std::string& device) {
     if (!android::base::WriteStringToFile(device, ZRAM_BACK_DEV)) {
         PERROR << "Cannot write " << device << " in: " << ZRAM_BACK_DEV;
@@ -2065,22 +2104,26 @@
 
     // Allocate loop device and attach it to file_path.
     LoopControl loop_control;
-    std::string device;
-    if (!loop_control.Attach(target_fd.get(), 5s, &device)) {
+    std::string loop_device;
+    if (!loop_control.Attach(target_fd.get(), 5s, &loop_device)) {
         return false;
     }
 
+    ConfigureIoScheduler(loop_device);
+
+    ConfigureQueueDepth(loop_device, "/");
+
     // set block size & direct IO
-    unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
-    if (device_fd.get() == -1) {
-        PERROR << "Cannot open " << device;
+    unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loop_device.c_str(), O_RDWR | O_CLOEXEC)));
+    if (loop_fd.get() == -1) {
+        PERROR << "Cannot open " << loop_device;
         return false;
     }
-    if (!LoopControl::EnableDirectIo(device_fd.get())) {
+    if (!LoopControl::EnableDirectIo(loop_fd.get())) {
         return false;
     }
 
-    return InstallZramDevice(device);
+    return InstallZramDevice(loop_device);
 }
 
 bool fs_mgr_swapon_all(const Fstab& fstab) {
@@ -2322,7 +2365,24 @@
         return false;
     }
 
-    auto options = "lowerdir=" + entry.lowerdir;
+    auto lowerdir = entry.lowerdir;
+    if (entry.fs_mgr_flags.overlayfs_remove_missing_lowerdir) {
+        bool removed_any = false;
+        std::vector<std::string> lowerdirs;
+        for (const auto& dir : android::base::Split(entry.lowerdir, ":")) {
+            if (access(dir.c_str(), F_OK)) {
+                PWARNING << __FUNCTION__ << "(): remove missing lowerdir '" << dir << "'";
+                removed_any = true;
+            } else {
+                lowerdirs.push_back(dir);
+            }
+        }
+        if (removed_any) {
+            lowerdir = android::base::Join(lowerdirs, ":");
+        }
+    }
+
+    auto options = "lowerdir=" + lowerdir;
     if (overlayfs_valid_result == OverlayfsValidResult::kOverrideCredsRequired) {
         options += ",override_creds=off";
     }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index d0c89b9..9b6c3dd 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -181,6 +181,7 @@
         CheckFlag("fsverity", fs_verity);
         CheckFlag("metadata_csum", ext_meta_csum);
         CheckFlag("fscompress", fs_compress);
+        CheckFlag("overlayfs_remove_missing_lowerdir", overlayfs_remove_missing_lowerdir);
 
 #undef CheckFlag
 
@@ -413,17 +414,24 @@
     return fstab_result;
 }
 
-// Identify path to fstab file. Lookup is based on pattern
-// fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in
-// folders /odm/etc, vendor/etc, or /.
+// 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
+// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
+// the first stage ramdisk during early boot.  Previously, the first stage
+// ramdisk's copy of the fstab had to be located in the root directory, but now
+// the system/etc directory is supported too and is the preferred location.
 std::string GetFstabPath() {
     for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
         std::string suffix;
 
         if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
 
-        for (const char* prefix :
-             {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab.", "/first_stage_ramdisk/fstab."}) {
+        for (const char* prefix : {// late-boot/post-boot locations
+                                   "/odm/etc/fstab.", "/vendor/etc/fstab.",
+                                   // early boot locations
+                                   "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
+                                   "/fstab.", "/first_stage_ramdisk/fstab."}) {
             std::string fstab_path = prefix + suffix;
             if (access(fstab_path.c_str(), F_OK) == 0) {
                 return fstab_path;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 4d32bda..0522ac5 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -1133,7 +1133,7 @@
         return false;
     }
     if (!images->BackingImageExists(partition_name)) {
-        static constexpr uint64_t kMinimumSize = 16_MiB;
+        static constexpr uint64_t kMinimumSize = 64_MiB;
         static constexpr uint64_t kMaximumSize = 2_GiB;
 
         uint64_t size = std::clamp(info.size / 2, kMinimumSize, kMaximumSize);
diff --git a/fs_mgr/include/fs_mgr/file_wait.h b/fs_mgr/include/fs_mgr/file_wait.h
index 74d160e..294e727 100644
--- a/fs_mgr/include/fs_mgr/file_wait.h
+++ b/fs_mgr/include/fs_mgr/file_wait.h
@@ -23,6 +23,9 @@
 // Wait at most |relative_timeout| milliseconds for |path| to exist. dirname(path)
 // must already exist. For example, to wait on /dev/block/dm-6, /dev/block must
 // be a valid directory.
+//
+// If relative_timeout is std::chrono::milliseconds::max(), then the wait will
+// block indefinitely.
 bool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout);
 
 // Wait at most |relative_timeout| milliseconds for |path| to stop existing.
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index f33768b..9a4ed46 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -86,6 +86,7 @@
         bool fs_verity : 1;
         bool ext_meta_csum : 1;
         bool fs_compress : 1;
+        bool overlayfs_remove_missing_lowerdir : 1;
     } fs_mgr_flags = {};
 
     bool is_encryptable() const {
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index a5eda29..4034e30 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -578,34 +578,30 @@
     return std::string{spec.target_type, sizeof(spec.target_type)};
 }
 
-static bool ExtractBlockDeviceName(const std::string& path, std::string* name) {
+std::optional<std::string> ExtractBlockDeviceName(const std::string& path) {
     static constexpr std::string_view kDevBlockPrefix("/dev/block/");
     if (android::base::StartsWith(path, kDevBlockPrefix)) {
-        *name = path.substr(kDevBlockPrefix.length());
-        return true;
+        return path.substr(kDevBlockPrefix.length());
     }
-    return false;
+    return {};
 }
 
 bool DeviceMapper::IsDmBlockDevice(const std::string& path) {
-    std::string name;
-    if (!ExtractBlockDeviceName(path, &name)) {
-        return false;
-    }
-    return android::base::StartsWith(name, "dm-");
+    std::optional<std::string> name = ExtractBlockDeviceName(path);
+    return name && android::base::StartsWith(*name, "dm-");
 }
 
 std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) {
-    std::string name;
-    if (!ExtractBlockDeviceName(path, &name)) {
+    std::optional<std::string> name = ExtractBlockDeviceName(path);
+    if (!name) {
         LOG(WARNING) << path << " is not a block device";
         return std::nullopt;
     }
-    if (!android::base::StartsWith(name, "dm-")) {
+    if (!android::base::StartsWith(*name, "dm-")) {
         LOG(WARNING) << path << " is not a dm device";
         return std::nullopt;
     }
-    std::string dm_name_file = "/sys/block/" + name + "/dm/name";
+    std::string dm_name_file = "/sys/block/" + *name + "/dm/name";
     std::string dm_name;
     if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
         PLOG(ERROR) << "Failed to read file " << dm_name_file;
@@ -616,16 +612,16 @@
 }
 
 std::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) {
-    std::string name;
-    if (!ExtractBlockDeviceName(path, &name)) {
+    std::optional<std::string> name = ExtractBlockDeviceName(path);
+    if (!name) {
         LOG(WARNING) << path << " is not a block device";
         return std::nullopt;
     }
-    if (!android::base::StartsWith(name, "dm-")) {
+    if (!android::base::StartsWith(*name, "dm-")) {
         // Reached bottom of the device mapper stack.
         return std::nullopt;
     }
-    auto slaves_dir = "/sys/block/" + name + "/slaves";
+    auto slaves_dir = "/sys/block/" + *name + "/slaves";
     auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir);
     if (dir == nullptr) {
         PLOG(ERROR) << "Failed to open: " << slaves_dir;
@@ -651,5 +647,61 @@
     return spec.target_type == "snapshot"s && data == "Overflow"s;
 }
 
+// Find directories in format of "/sys/block/dm-X".
+static int DmNameFilter(const dirent* de) {
+    if (android::base::StartsWith(de->d_name, "dm-")) {
+        return 1;
+    }
+    return 0;
+}
+
+std::map<std::string, std::string> DeviceMapper::FindDmPartitions() {
+    static constexpr auto DM_PATH_PREFIX = "/sys/block/";
+    dirent** namelist;
+    int n = scandir(DM_PATH_PREFIX, &namelist, DmNameFilter, alphasort);
+    if (n == -1) {
+        PLOG(ERROR) << "Failed to scan dir " << DM_PATH_PREFIX;
+        return {};
+    }
+    if (n == 0) {
+        LOG(ERROR) << "No dm block device found.";
+        free(namelist);
+        return {};
+    }
+
+    static constexpr auto DM_PATH_SUFFIX = "/dm/name";
+    static constexpr auto DEV_PATH = "/dev/block/";
+    std::map<std::string, std::string> dm_block_devices;
+    while (n--) {
+        std::string path = DM_PATH_PREFIX + std::string(namelist[n]->d_name) + DM_PATH_SUFFIX;
+        std::string content;
+        if (!android::base::ReadFileToString(path, &content)) {
+            PLOG(WARNING) << "Failed to read " << path;
+        } else {
+            std::string dm_block_name = android::base::Trim(content);
+            // AVB is using 'vroot' for the root block device but we're expecting 'system'.
+            if (dm_block_name == "vroot") {
+                dm_block_name = "system";
+            } else if (android::base::EndsWith(dm_block_name, "-verity")) {
+                auto npos = dm_block_name.rfind("-verity");
+                dm_block_name = dm_block_name.substr(0, npos);
+            } else if (!android::base::GetProperty("ro.boot.avb_version", "").empty()) {
+                // Verified Boot 1.0 doesn't add a -verity suffix. On AVB 2 devices,
+                // if DAP is enabled, then a -verity suffix must be used to
+                // differentiate between dm-linear and dm-verity devices. If we get
+                // here, we're AVB 2 and looking at a non-verity partition.
+                free(namelist[n]);
+                continue;
+            }
+
+            dm_block_devices.emplace(dm_block_name, DEV_PATH + std::string(namelist[n]->d_name));
+        }
+        free(namelist[n]);
+    }
+    free(namelist);
+
+    return dm_block_devices;
+}
+
 }  // namespace dm
 }  // namespace android
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 8fcdf74..7e01b85 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -17,6 +17,7 @@
 #ifndef _LIBDM_DM_H_
 #define _LIBDM_DM_H_
 
+#include <dirent.h>
 #include <fcntl.h>
 #include <linux/dm-ioctl.h>
 #include <linux/kdev_t.h>
@@ -26,6 +27,7 @@
 #include <unistd.h>
 
 #include <chrono>
+#include <map>
 #include <memory>
 #include <optional>
 #include <string>
@@ -49,6 +51,10 @@
 
 static constexpr uint64_t kSectorSize = 512;
 
+// Returns `path` without /dev/block prefix if and only if `path` starts with
+// that prefix.
+std::optional<std::string> ExtractBlockDeviceName(const std::string& path);
+
 class DeviceMapper final {
   public:
     class DmBlockDevice final {
@@ -255,6 +261,12 @@
     //  * A failure occurred.
     std::optional<std::string> GetParentBlockDeviceByPath(const std::string& path);
 
+    // Iterate the content over "/sys/block/dm-x/dm/name" and find
+    // all the dm-wrapped block devices.
+    //
+    // Returns mapping <partition-name, /dev/block/dm-x>
+    std::map<std::string, std::string> FindDmPartitions();
+
   private:
     // Maximum possible device mapper targets registered in the kernel.
     // This is only used to read the list of targets from kernel so we allocate
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index b62e33f..d16b8d6 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -21,6 +21,7 @@
 cc_library_headers {
     name: "libfiemap_headers",
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     export_include_dirs: ["include"],
 }
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 3c8ab42..b31c78d 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -16,6 +16,7 @@
 
 #include <fcntl.h>
 #include <inttypes.h>
+#include <linux/limits.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -299,6 +300,27 @@
     ASSERT_EQ(errno, ENOENT);
 }
 
+TEST_F(SplitFiemapTest, CorruptSplit) {
+    unique_fd fd(open(testfile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0700));
+    ASSERT_GE(fd, 0);
+
+    // Make a giant random string.
+    std::vector<char> data;
+    for (size_t i = 0x1; i < 0x7f; i++) {
+        for (size_t j = 0; j < 100; j++) {
+            data.emplace_back(i);
+        }
+    }
+    ASSERT_GT(data.size(), PATH_MAX);
+
+    data.emplace_back('\n');
+
+    ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
+    fd = {};
+
+    ASSERT_TRUE(SplitFiemap::RemoveSplitFiles(testfile));
+}
+
 static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
     std::string result;
     for (int i = 0; i < num_files; i++) {
diff --git a/fs_mgr/libfiemap/split_fiemap_writer.cpp b/fs_mgr/libfiemap/split_fiemap_writer.cpp
index 8457066..0df6125 100644
--- a/fs_mgr/libfiemap/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/split_fiemap_writer.cpp
@@ -193,6 +193,9 @@
     std::vector<std::string> files;
     if (GetSplitFileList(file_path, &files)) {
         for (const auto& file : files) {
+            if (access(file.c_str(), F_OK) != 0 && (errno == ENOENT || errno == ENAMETOOLONG)) {
+                continue;
+            }
             ok &= android::base::RemoveFileIfExists(file, message);
         }
     }
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 62493eb..0cbd9db 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -28,6 +28,7 @@
     name: "libfs_avb",
     defaults: ["fs_mgr_defaults"],
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     host_supported: true,
     export_include_dirs: ["include"],
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 86ca8f3..fc2d8a1 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -31,6 +31,7 @@
     name: "liblp",
     host_supported: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     defaults: ["fs_mgr_defaults"],
     cppflags: [
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index acfaa84..df00f45 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -182,38 +182,6 @@
     vendor_ramdisk_available: true,
 }
 
-cc_defaults {
-    name: "libsnapshot_snapuserd_defaults",
-    defaults: [
-        "fs_mgr_defaults",
-    ],
-    cflags: [
-        "-D_FILE_OFFSET_BITS=64",
-        "-Wall",
-        "-Werror",
-    ],
-    export_include_dirs: ["include"],
-    srcs: [
-        "snapuserd_client.cpp",
-    ],
-}
-
-cc_library_static {
-    name: "libsnapshot_snapuserd",
-    defaults: [
-        "libsnapshot_snapuserd_defaults",
-    ],
-    recovery_available: true,
-    static_libs: [
-        "libcutils_sockets",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-    ramdisk_available: true,
-}
-
 cc_library_static {
     name: "libsnapshot_test_helpers",
     defaults: ["libsnapshot_defaults"],
@@ -412,49 +380,6 @@
     require_root: true,
 }
 
-cc_defaults {
-    name: "snapuserd_defaults",
-    defaults: [
-        "fs_mgr_defaults",
-    ],
-    srcs: [
-        "snapuserd_server.cpp",
-        "snapuserd.cpp",
-        "snapuserd_daemon.cpp",
-        "snapuserd_worker.cpp",
-        "snapuserd_readahead.cpp",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Werror"
-    ],
-
-    static_libs: [
-        "libbase",
-        "libbrotli",
-        "libcutils_sockets",
-        "libdm",
-        "libgflags",
-        "liblog",
-        "libsnapshot_cow",
-        "libz",
-    ],
-}
-
-cc_binary {
-    name: "snapuserd",
-    defaults: ["snapuserd_defaults"],
-    init_rc: [
-        "snapuserd.rc",
-    ],
-    static_executable: true,
-    system_shared_libs: [],
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
-    recovery_available: true,
-}
-
 cc_test {
     name: "cow_api_test",
     defaults: [
@@ -556,43 +481,6 @@
     },
 }
 
-cc_test {
-    name: "cow_snapuserd_test",
-    defaults: [
-        "fs_mgr_defaults",
-    ],
-    srcs: [
-        "cow_snapuserd_test.cpp",
-        "snapuserd.cpp",
-        "snapuserd_worker.cpp",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-    static_libs: [
-        "libbrotli",
-        "libgtest",
-        "libsnapshot_cow",
-        "libsnapshot_snapuserd",
-        "libcutils_sockets",
-        "libz",
-        "libfs_mgr",
-        "libdm",
-    ],
-    header_libs: [
-        "libstorage_literals_headers",
-        "libfiemap_headers",
-    ],
-    test_min_api_level: 30,
-    auto_gen_config: true,
-    require_root: false,
-}
-
 cc_binary {
     name: "inspect_cow",
     host_supported: true,
@@ -616,3 +504,13 @@
         "inspect_cow.cpp",
     ],
 }
+
+python_library_host {
+    name: "snapshot_proto_python",
+    srcs: [
+        "android/snapshot/snapshot.proto",
+    ],
+    proto: {
+        canonical_path_from_root: false,
+    },
+}
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index 7f7e40a..6066309 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -140,6 +140,85 @@
     ASSERT_TRUE(iter->Done());
 }
 
+TEST_F(CowTest, ReadWriteXor) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer.AddCopy(10, 20));
+    ASSERT_TRUE(writer.AddXorBlocks(50, data.data(), data.size(), 24, 10));
+    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    CowHeader header;
+    CowFooter footer;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_TRUE(reader.GetHeader(&header));
+    ASSERT_TRUE(reader.GetFooter(&footer));
+    ASSERT_EQ(header.magic, kCowMagicNumber);
+    ASSERT_EQ(header.major_version, kCowVersionMajor);
+    ASSERT_EQ(header.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+    ASSERT_EQ(footer.op.num_ops, 4);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->Done());
+    auto op = &iter->Get();
+
+    ASSERT_EQ(op->type, kCowCopyOp);
+    ASSERT_EQ(op->compression, kCowCompressNone);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 10);
+    ASSERT_EQ(op->source, 20);
+
+    StringSink sink;
+
+    iter->Next();
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+
+    ASSERT_EQ(op->type, kCowXorOp);
+    ASSERT_EQ(op->compression, kCowCompressNone);
+    ASSERT_EQ(op->data_length, 4096);
+    ASSERT_EQ(op->new_block, 50);
+    ASSERT_EQ(op->source, 98314);  // 4096 * 24 + 10
+    ASSERT_TRUE(reader.ReadData(*op, &sink));
+    ASSERT_EQ(sink.stream(), data);
+
+    iter->Next();
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+
+    // Note: the zero operation gets split into two blocks.
+    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->compression, kCowCompressNone);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 51);
+    ASSERT_EQ(op->source, 0);
+
+    iter->Next();
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+
+    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->compression, kCowCompressNone);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 52);
+    ASSERT_EQ(op->source, 0);
+
+    iter->Next();
+    ASSERT_TRUE(iter->Done());
+}
+
 TEST_F(CowTest, CompressGz) {
     CowOptions options;
     options.cluster_ops = 0;
@@ -1013,6 +1092,75 @@
     ASSERT_TRUE(iter->Done());
 }
 
+TEST_F(CowTest, MissingSeqOp) {
+    CowOptions options;
+    CowWriter writer(options);
+    const int seq_len = 10;
+    uint32_t sequence[seq_len];
+    for (int i = 0; i < seq_len; i++) {
+        sequence[i] = i + 1;
+    }
+
+    ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+    ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
+    ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len - 1));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_FALSE(reader.Parse(cow_->fd));
+}
+
+TEST_F(CowTest, ResumeSeqOp) {
+    CowOptions options;
+    auto writer = std::make_unique<CowWriter>(options);
+    const int seq_len = 10;
+    uint32_t sequence[seq_len];
+    for (int i = 0; i < seq_len; i++) {
+        sequence[i] = i + 1;
+    }
+
+    ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+    ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+    ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));
+    ASSERT_TRUE(writer->AddLabel(1));
+    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, 1));
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+    auto reader = std::make_unique<CowReader>();
+    ASSERT_TRUE(reader->Parse(cow_->fd, 1));
+    auto itr = reader->GetRevMergeOpIter();
+    ASSERT_TRUE(itr->Done());
+
+    writer = std::make_unique<CowWriter>(options);
+    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    reader = std::make_unique<CowReader>();
+    ASSERT_TRUE(reader->Parse(cow_->fd));
+
+    auto iter = reader->GetRevMergeOpIter();
+
+    uint64_t expected_block = 10;
+    while (!iter->Done() && expected_block > 0) {
+        ASSERT_FALSE(iter->Done());
+        const auto& op = iter->Get();
+
+        ASSERT_EQ(op.new_block, expected_block);
+
+        iter->Next();
+        expected_block--;
+    }
+    ASSERT_EQ(expected_block, 0);
+    ASSERT_TRUE(iter->Done());
+}
+
 TEST_F(CowTest, RevMergeOpItrTest) {
     CowOptions options;
     options.cluster_ops = 5;
diff --git a/fs_mgr/libsnapshot/cow_format.cpp b/fs_mgr/libsnapshot/cow_format.cpp
index 3085f80..8e6bec7 100644
--- a/fs_mgr/libsnapshot/cow_format.cpp
+++ b/fs_mgr/libsnapshot/cow_format.cpp
@@ -37,6 +37,8 @@
         os << "kCowLabelOp,   ";
     else if (op.type == kCowClusterOp)
         os << "kCowClusterOp  ";
+    else if (op.type == kCowXorOp)
+        os << "kCowXorOp      ";
     else if (op.type == kCowSequenceOp)
         os << "kCowSequenceOp ";
     else if (op.type == kCowFooterOp)
@@ -61,7 +63,7 @@
 int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_ops) {
     if (op.type == kCowClusterOp) {
         return op.source;
-    } else if (op.type == kCowReplaceOp && cluster_ops == 0) {
+    } else if ((op.type == kCowReplaceOp || op.type == kCowXorOp) && cluster_ops == 0) {
         return op.data_length;
     } else {
         return 0;
@@ -93,6 +95,7 @@
 bool IsOrderedOp(const CowOperation& op) {
     switch (op.type) {
         case kCowCopyOp:
+        case kCowXorOp:
             return true;
         default:
             return false;
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index af49c7d..773d978 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -34,7 +34,11 @@
 namespace android {
 namespace snapshot {
 
-CowReader::CowReader() : fd_(-1), header_(), fd_size_(0) {}
+CowReader::CowReader()
+    : fd_(-1),
+      header_(),
+      fd_size_(0),
+      merge_op_blocks_(std::make_shared<std::vector<uint32_t>>()) {}
 
 static void SHA256(const void*, size_t, uint8_t[]) {
 #if 0
@@ -45,6 +49,23 @@
 #endif
 }
 
+std::unique_ptr<CowReader> CowReader::CloneCowReader() {
+    auto cow = std::make_unique<CowReader>();
+    cow->owned_fd_.reset();
+    cow->header_ = header_;
+    cow->footer_ = footer_;
+    cow->fd_size_ = fd_size_;
+    cow->last_label_ = last_label_;
+    cow->ops_ = ops_;
+    cow->merge_op_blocks_ = merge_op_blocks_;
+    cow->block_map_ = block_map_;
+    cow->num_total_data_ops_ = num_total_data_ops_;
+    cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
+    cow->has_seq_ops_ = has_seq_ops_;
+    cow->data_loc_ = data_loc_;
+    return cow;
+}
+
 bool CowReader::InitForMerge(android::base::unique_fd&& fd) {
     owned_fd_ = std::move(fd);
     fd_ = owned_fd_.get();
@@ -133,11 +154,14 @@
     if (!ParseOps(label)) {
         return false;
     }
+    // If we're resuming a write, we're not ready to merge
+    if (label.has_value()) return true;
     return PrepMergeOps();
 }
 
 bool CowReader::ParseOps(std::optional<uint64_t> label) {
     uint64_t pos;
+    auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
 
     // Skip the scratch space
     if (header_.major_version >= 2 && (header_.buffer_size > 0)) {
@@ -157,6 +181,13 @@
         // Reading a v1 version of COW which doesn't have buffer_size.
         header_.buffer_size = 0;
     }
+    uint64_t data_pos = 0;
+
+    if (header_.cluster_ops) {
+        data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
+    } else {
+        data_pos = pos + sizeof(CowOperation);
+    }
 
     auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
     uint64_t current_op_num = 0;
@@ -177,7 +208,11 @@
         while (current_op_num < ops_buffer->size()) {
             auto& current_op = ops_buffer->data()[current_op_num];
             current_op_num++;
+            if (current_op.type == kCowXorOp) {
+                data_loc->insert({current_op.new_block, data_pos});
+            }
             pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
+            data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
 
             if (current_op.type == kCowClusterOp) {
                 break;
@@ -268,6 +303,7 @@
 
     ops_ = ops_buffer;
     ops_->shrink_to_fit();
+    data_loc_ = data_loc;
 
     return true;
 }
@@ -413,6 +449,13 @@
         }
         block_map->insert({current_op.new_block, i});
     }
+    for (auto block : *merge_op_blocks) {
+        if (block_map->count(block) == 0) {
+            LOG(ERROR) << "Invalid Sequence Ops. Could not find Cow Op for new block " << block;
+            return false;
+        }
+    }
+
     if (merge_op_blocks->size() > header_.num_merge_ops) {
         num_ordered_ops_to_merge_ = merge_op_blocks->size() - header_.num_merge_ops;
     } else {
@@ -599,7 +642,13 @@
             return false;
     }
 
-    CowDataStream stream(this, op.source, op.data_length);
+    uint64_t offset;
+    if (op.type == kCowXorOp) {
+        offset = data_loc_->at(op.new_block);
+    } else {
+        offset = op.source;
+    }
+    CowDataStream stream(this, offset, op.data_length);
     decompressor->set_stream(&stream);
     decompressor->set_sink(sink);
     return decompressor->Decompress(header_.block_size);
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index ef30e32..5ce1d3b 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -58,10 +58,24 @@
     return EmitRawBlocks(new_block_start, data, size);
 }
 
-bool ICowWriter::AddXorBlocks(uint32_t /*new_block_start*/, const void* /*data*/, size_t /*size*/,
-                              uint32_t /*old_block*/, uint16_t /*offset*/) {
-    LOG(ERROR) << "AddXorBlocks not yet implemented";
-    return false;
+bool ICowWriter::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+                              uint32_t old_block, uint16_t offset) {
+    if (size % options_.block_size != 0) {
+        LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+                   << options_.block_size;
+        return false;
+    }
+
+    uint64_t num_blocks = size / options_.block_size;
+    uint64_t last_block = new_block_start + num_blocks - 1;
+    if (!ValidateNewBlock(last_block)) {
+        return false;
+    }
+    if (offset >= options_.block_size) {
+        LOG(ERROR) << "AddXorBlocks: offset " << offset << " is not less than "
+                   << options_.block_size;
+    }
+    return EmitXorBlocks(new_block_start, data, size, old_block, offset);
 }
 
 bool ICowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
@@ -278,13 +292,27 @@
 }
 
 bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+    return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);
+}
+
+bool CowWriter::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+                              uint32_t old_block, uint16_t offset) {
+    return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
+}
+
+bool CowWriter::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
+                           uint64_t old_block, uint16_t offset, uint8_t type) {
     const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
     CHECK(!merge_in_progress_);
     for (size_t i = 0; i < size / header_.block_size; i++) {
         CowOperation op = {};
-        op.type = kCowReplaceOp;
         op.new_block = new_block_start + i;
-        op.source = next_data_pos_;
+        op.type = type;
+        if (type == kCowXorOp) {
+            op.source = (old_block + i) * header_.block_size + offset;
+        } else {
+            op.source = next_data_pos_;
+        }
 
         if (compression_) {
             auto data = Compress(iter, header_.block_size);
diff --git a/fs_mgr/libsnapshot/dm_snapshot_internals.h b/fs_mgr/libsnapshot/dm_snapshot_internals.h
index ed77c15..4a36251 100644
--- a/fs_mgr/libsnapshot/dm_snapshot_internals.h
+++ b/fs_mgr/libsnapshot/dm_snapshot_internals.h
@@ -17,8 +17,9 @@
 #include <android-base/logging.h>
 #include <stdint.h>
 
+#include <limits>
 #include <optional>
-#include <vector>
+#include <unordered_set>
 
 namespace android {
 namespace snapshot {
@@ -37,21 +38,16 @@
             return;
         }
 
-        if (modified_chunks_.size() <= chunk_id) {
-            if (modified_chunks_.max_size() <= chunk_id) {
-                LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
-                valid_ = false;
-                return;
-            }
-            modified_chunks_.resize(chunk_id + 1, false);
-            if (modified_chunks_.size() <= chunk_id) {
-                LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
-                valid_ = false;
-                return;
-            }
+        if (chunk_id > std::numeric_limits<uint32_t>::max()) {
+            LOG(ERROR) << "Chunk exceeds maximum size: " << chunk_id;
+            valid_ = false;
+            return;
+        }
+        if (modified_chunks_.count(chunk_id) > 0) {
+            return;
         }
 
-        modified_chunks_[chunk_id] = true;
+        modified_chunks_.emplace(chunk_id);
     }
 
     std::optional<uint64_t> cow_size_bytes() const {
@@ -91,23 +87,16 @@
             return std::nullopt;
         }
 
-        uint64_t modified_chunks_count = 0;
         uint64_t cow_chunks = 0;
 
-        for (const auto& c : modified_chunks_) {
-            if (c) {
-                ++modified_chunks_count;
-            }
-        }
-
         /* disk header + padding = 1 chunk */
         cow_chunks += 1;
 
         /* snapshot modified chunks */
-        cow_chunks += modified_chunks_count;
+        cow_chunks += modified_chunks_.size();
 
         /* snapshot chunks index metadata */
-        cow_chunks += 1 + modified_chunks_count / exceptions_per_chunk;
+        cow_chunks += 1 + modified_chunks_.size() / exceptions_per_chunk;
 
         return cow_chunks;
     }
@@ -150,30 +139,8 @@
     /*
      * |modified_chunks_| is a container that keeps trace of the modified
      * chunks.
-     * Multiple options were considered when choosing the most appropriate data
-     * structure for this container. Here follows a summary of why vector<bool>
-     * has been chosen, taking as a reference a snapshot partition of 4 GiB and
-     * chunk size of 4 KiB.
-     * - std::set<uint64_t> is very space-efficient for a small number of
-     *   operations, but if the whole snapshot is changed, it would need to
-     *   store
-     *     4 GiB / 4 KiB * (64 bit / 8) = 8 MiB
-     *   just for the data, plus the additional data overhead for the red-black
-     *   tree used for data sorting (if each rb-tree element stores 3 address
-     *   and the word-aligne color, the total size grows to 32 MiB).
-     * - std::bitset<N> is not a good fit because requires a priori knowledge,
-     *   at compile time, of the bitset size.
-     * - std::vector<bool> is a special case of vector, which uses a data
-     *   compression that allows reducing the space utilization of each element
-     *   to 1 bit. In detail, this data structure is composed of a resizable
-     *   array of words, each of them representing a bitmap. On a 64 bit
-     *   device, modifying the whole 4 GiB snapshot grows this container up to
-     *     4 * GiB / 4 KiB / 64 = 64 KiB
-     *   that, even if is the same space requirement to change a single byte at
-     *   the highest address of the snapshot, is a very affordable space
-     *   requirement.
      */
-    std::vector<bool> modified_chunks_;
+    std::unordered_set<uint32_t> modified_chunks_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 464046b..c15682a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -138,6 +138,8 @@
     // For Label operations, this is the value of the applied label.
     //
     // For Cluster operations, this is the length of the following data region
+    //
+    // For Xor operations, this is the byte location in the source image.
     uint64_t source;
 } __attribute__((packed));
 
@@ -148,6 +150,7 @@
 static constexpr uint8_t kCowZeroOp = 3;
 static constexpr uint8_t kCowLabelOp = 4;
 static constexpr uint8_t kCowClusterOp = 5;
+static constexpr uint8_t kCowXorOp = 6;
 static constexpr uint8_t kCowSequenceOp = 7;
 static constexpr uint8_t kCowFooterOp = -1;
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 6c3059c..0786e82 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -136,6 +136,9 @@
 
     void CloseCowFd() { owned_fd_ = {}; }
 
+    // Creates a clone of the current CowReader without the file handlers
+    std::unique_ptr<CowReader> CloneCowReader();
+
   private:
     bool ParseOps(std::optional<uint64_t> label);
     bool PrepMergeOps();
@@ -153,6 +156,7 @@
     uint64_t num_total_data_ops_;
     uint64_t num_ordered_ops_to_merge_;
     bool has_seq_ops_;
+    std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 4a807fb..e17b5c6 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -86,6 +86,8 @@
   protected:
     virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) = 0;
     virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+    virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+                               uint32_t old_block, uint16_t offset) = 0;
     virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
     virtual bool EmitLabel(uint64_t label) = 0;
     virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) = 0;
@@ -122,6 +124,8 @@
   protected:
     virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
     virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+    virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+                               uint32_t old_block, uint16_t offset) override;
     virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
     virtual bool EmitLabel(uint64_t label) override;
     virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
@@ -129,6 +133,8 @@
   private:
     bool EmitCluster();
     bool EmitClusterIfNeeded();
+    bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
+                    uint16_t offset, uint8_t type);
     void SetupHeaders();
     bool ParseOptions();
     bool OpenForWrite();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 15882b3..e60da31 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -38,7 +38,7 @@
 #include <libsnapshot/auto_device.h>
 #include <libsnapshot/return.h>
 #include <libsnapshot/snapshot_writer.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index c00dafa..b09e1ae 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -74,6 +74,8 @@
   protected:
     bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
     bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+    bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+                       uint16_t offset) override;
     bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
     bool EmitLabel(uint64_t label) override;
     bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
@@ -102,6 +104,8 @@
   protected:
     bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
     bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+    bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+                       uint16_t offset) override;
     bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
     bool EmitLabel(uint64_t label) override;
     bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
diff --git a/fs_mgr/libsnapshot/scripts/Android.bp b/fs_mgr/libsnapshot/scripts/Android.bp
new file mode 100644
index 0000000..3e09cc3
--- /dev/null
+++ b/fs_mgr/libsnapshot/scripts/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2021 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.
+//
+
+python_binary_host {
+    name: "dump_snapshot_proto",
+    main: "dump_snapshot_proto.py",
+    srcs: [
+        "dump_snapshot_proto.py",
+    ],
+    libs: [
+        "snapshot_proto_python",
+    ],
+}
diff --git a/fs_mgr/libsnapshot/scripts/dump_snapshot_proto.py b/fs_mgr/libsnapshot/scripts/dump_snapshot_proto.py
new file mode 100644
index 0000000..566108d
--- /dev/null
+++ b/fs_mgr/libsnapshot/scripts/dump_snapshot_proto.py
@@ -0,0 +1,39 @@
+# Copyright (C) 2021 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.
+
+import argparse
+
+from android.snapshot import snapshot_pb2
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('type', type = str, help = 'Type (snapshot or update)')
+    parser.add_argument('file', type = str, help = 'Input file')
+    args = parser.parse_args()
+
+    with open(args.file, 'rb') as fp:
+        data = fp.read()
+
+    if args.type == 'snapshot':
+        msg = snapshot_pb2.SnapshotStatus()
+    elif args.type == 'update':
+        msg = snapshot_pb2.SnapshotUpdateStatus()
+    else:
+        raise Exception('Unknown proto type')
+
+    msg.ParseFromString(data)
+    print(msg)
+
+if __name__ == '__main__':
+    main()
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
index 5ee8e25..6546c2a 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader.cpp
@@ -221,7 +221,7 @@
 
   private:
     size_t ignore_start_;
-    char discard_[4096];
+    char discard_[BLOCK_SZ];
 };
 
 ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
@@ -277,6 +277,29 @@
             errno = EIO;
             return -1;
         }
+    } else if (op->type == kCowXorOp) {
+        borrowed_fd fd = GetSourceFd();
+        if (fd < 0) {
+            // GetSourceFd sets errno.
+            return -1;
+        }
+
+        off64_t offset = op->source + start_offset;
+        char data[BLOCK_SZ];
+        if (!android::base::ReadFullyAtOffset(fd, &data, bytes_to_read, offset)) {
+            PLOG(ERROR) << "read " << *source_device_;
+            // ReadFullyAtOffset sets errno.
+            return -1;
+        }
+        PartialSink partial_sink(buffer, bytes_to_read, start_offset);
+        if (!cow_->ReadData(*op, &partial_sink)) {
+            LOG(ERROR) << "CompressedSnapshotReader failed to read xor op";
+            errno = EIO;
+            return -1;
+        }
+        for (size_t i = 0; i < bytes_to_read; i++) {
+            ((char*)buffer)[i] ^= data[i];
+        }
     } else {
         LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type);
         errno = EINVAL;
diff --git a/fs_mgr/libsnapshot/snapshot_reader_test.cpp b/fs_mgr/libsnapshot/snapshot_reader_test.cpp
index 9373059..078f16e 100644
--- a/fs_mgr/libsnapshot/snapshot_reader_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader_test.cpp
@@ -63,7 +63,9 @@
 
     void WriteCow(ISnapshotWriter* writer) {
         std::string new_block = MakeNewBlockString();
+        std::string xor_block = MakeXorBlockString();
 
+        ASSERT_TRUE(writer->AddXorBlocks(1, xor_block.data(), xor_block.size(), 0, kBlockSize / 2));
         ASSERT_TRUE(writer->AddCopy(3, 0));
         ASSERT_TRUE(writer->AddRawBlocks(5, new_block.data(), new_block.size()));
         ASSERT_TRUE(writer->AddZeroBlocks(7, 2));
@@ -75,7 +77,7 @@
         ASSERT_NE(reader, nullptr);
 
         // Test that unchanged blocks are not modified.
-        std::unordered_set<size_t> changed_blocks = {3, 5, 7, 8};
+        std::unordered_set<size_t> changed_blocks = {1, 3, 5, 7, 8};
         for (size_t i = 0; i < kBlockCount; i++) {
             if (changed_blocks.count(i)) {
                 continue;
@@ -88,6 +90,17 @@
         }
 
         // Test that we can read back our modified blocks.
+        std::string data(kBlockSize, 0);
+        std::string offsetblock = base_blocks_[0].substr(kBlockSize / 2, kBlockSize / 2) +
+                                  base_blocks_[1].substr(0, kBlockSize / 2);
+        ASSERT_EQ(offsetblock.size(), kBlockSize);
+        ASSERT_EQ(reader->Seek(1 * kBlockSize, SEEK_SET), 1 * kBlockSize);
+        ASSERT_EQ(reader->Read(data.data(), data.size()), kBlockSize);
+        for (int i = 0; i < 100; i++) {
+            data[i] = (char)~(data[i]);
+        }
+        ASSERT_EQ(data, offsetblock);
+
         std::string block(kBlockSize, 0);
         ASSERT_EQ(reader->Seek(3 * kBlockSize, SEEK_SET), 3 * kBlockSize);
         ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
@@ -141,6 +154,12 @@
         return new_block;
     }
 
+    std::string MakeXorBlockString() {
+        std::string data(100, -1);
+        data.resize(kBlockSize, 0);
+        return data;
+    }
+
     std::unique_ptr<TemporaryFile> base_;
     std::unique_ptr<TemporaryFile> cow_;
     std::vector<std::string> base_blocks_;
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 34b3e87..3eda08e 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -106,6 +106,11 @@
     return cow_->AddRawBlocks(new_block_start, data, size);
 }
 
+bool CompressedSnapshotWriter::EmitXorBlocks(uint32_t new_block_start, const void* data,
+                                             size_t size, uint32_t old_block, uint16_t offset) {
+    return cow_->AddXorBlocks(new_block_start, data, size, old_block, offset);
+}
+
 bool CompressedSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
     return cow_->AddZeroBlocks(new_block_start, num_blocks);
 }
@@ -157,6 +162,11 @@
     return true;
 }
 
+bool OnlineKernelSnapshotWriter::EmitXorBlocks(uint32_t, const void*, size_t, uint32_t, uint16_t) {
+    LOG(ERROR) << "EmitXorBlocks not implemented.";
+    return false;
+}
+
 bool OnlineKernelSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
     std::string zeroes(options_.block_size, 0);
     for (uint64_t i = 0; i < num_blocks; i++) {
diff --git a/fs_mgr/libsnapshot/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd.rc
deleted file mode 100644
index 4bf34a2..0000000
--- a/fs_mgr/libsnapshot/snapuserd.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service snapuserd /system/bin/snapuserd
-    socket snapuserd stream 0660 system system
-    oneshot
-    disabled
-    user root
-    group root system
-    seclabel u:r:snapuserd:s0
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
new file mode 100644
index 0000000..47268d4
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -0,0 +1,134 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+    name: "libsnapshot_snapuserd_defaults",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
+    ],
+    export_include_dirs: ["include"],
+    srcs: [
+        "snapuserd_client.cpp",
+    ],
+}
+
+cc_library_static {
+    name: "libsnapshot_snapuserd",
+    defaults: [
+        "libsnapshot_snapuserd_defaults",
+    ],
+    recovery_available: true,
+    static_libs: [
+        "libcutils_sockets",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    ramdisk_available: true,
+}
+
+cc_defaults {
+    name: "snapuserd_defaults",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    srcs: [
+        "snapuserd_server.cpp",
+        "snapuserd.cpp",
+        "snapuserd_daemon.cpp",
+        "snapuserd_worker.cpp",
+        "snapuserd_readahead.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror"
+    ],
+
+    static_libs: [
+        "libbase",
+        "libbrotli",
+        "libcutils_sockets",
+        "libdm",
+        "libfs_mgr",
+        "libgflags",
+        "liblog",
+        "libsnapshot_cow",
+        "libz",
+        "libext4_utils",
+    ],
+}
+
+cc_binary {
+    name: "snapuserd",
+    defaults: ["snapuserd_defaults"],
+    init_rc: [
+        "snapuserd.rc",
+    ],
+    static_executable: true,
+    system_shared_libs: [],
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
+}
+
+cc_test {
+    name: "cow_snapuserd_test",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    srcs: [
+        "cow_snapuserd_test.cpp",
+        "snapuserd.cpp",
+        "snapuserd_worker.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libbrotli",
+        "libgtest",
+        "libsnapshot_cow",
+        "libsnapshot_snapuserd",
+        "libcutils_sockets",
+        "libz",
+        "libfs_mgr",
+        "libdm",
+        "libext4_utils",
+    ],
+    header_libs: [
+        "libstorage_literals_headers",
+        "libfiemap_headers",
+    ],
+    test_min_api_level: 30,
+    auto_gen_config: true,
+    require_root: false,
+}
diff --git a/fs_mgr/libsnapshot/snapuserd/OWNERS b/fs_mgr/libsnapshot/snapuserd/OWNERS
new file mode 100644
index 0000000..2df0a2d
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/OWNERS
@@ -0,0 +1,3 @@
+akailash@google.com
+dvander@google.com
+drosen@google.com
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
similarity index 76%
rename from fs_mgr/libsnapshot/cow_snapuserd_test.cpp
rename to fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
index 767cd04..f4aef44 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
@@ -33,7 +33,7 @@
 #include <libdm/dm.h>
 #include <libdm/loop_control.h>
 #include <libsnapshot/cow_writer.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 #include <storage_literals/storage_literals.h>
 
 #include "snapuserd.h"
@@ -96,6 +96,8 @@
 class CowSnapuserdTest final {
   public:
     bool Setup();
+    bool SetupOrderedOps();
+    bool SetupOrderedOpsInverted();
     bool SetupCopyOverlap_1();
     bool SetupCopyOverlap_2();
     bool Merge();
@@ -103,6 +105,8 @@
     void ReadSnapshotDeviceAndValidate();
     void Shutdown();
     void MergeInterrupt();
+    void MergeInterruptFixed(int duration);
+    void MergeInterruptRandomly(int max_duration);
     void ReadDmUserBlockWithoutDaemon();
 
     std::string snapshot_dev() const { return snapshot_dev_->path(); }
@@ -117,6 +121,8 @@
     void StartMerge();
 
     void CreateCowDevice();
+    void CreateCowDeviceOrderedOps();
+    void CreateCowDeviceOrderedOpsInverted();
     void CreateCowDeviceWithCopyOverlap_1();
     void CreateCowDeviceWithCopyOverlap_2();
     bool SetupDaemon();
@@ -197,6 +203,18 @@
     return setup_ok_;
 }
 
+bool CowSnapuserdTest::SetupOrderedOps() {
+    CreateBaseDevice();
+    CreateCowDeviceOrderedOps();
+    return SetupDaemon();
+}
+
+bool CowSnapuserdTest::SetupOrderedOpsInverted() {
+    CreateBaseDevice();
+    CreateCowDeviceOrderedOpsInverted();
+    return SetupDaemon();
+}
+
 bool CowSnapuserdTest::SetupCopyOverlap_1() {
     CreateBaseDevice();
     CreateCowDeviceWithCopyOverlap_1();
@@ -241,7 +259,7 @@
 void CowSnapuserdTest::CreateBaseDevice() {
     unique_fd rnd_fd;
 
-    total_base_size_ = (size_ * 4);
+    total_base_size_ = (size_ * 5);
     base_fd_ = CreateTempFile("base_device", total_base_size_);
     ASSERT_GE(base_fd_, 0);
 
@@ -286,6 +304,11 @@
     offset += size_;
     ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
     ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
+
+    // XOR
+    offset += size_;
+    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
 }
 
 void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
@@ -383,6 +406,130 @@
               true);
 }
 
+void CowSnapuserdTest::CreateCowDeviceOrderedOpsInverted() {
+    unique_fd rnd_fd;
+    loff_t offset = 0;
+
+    std::string path = android::base::GetExecutableDirectory();
+    cow_system_ = std::make_unique<TemporaryFile>(path);
+
+    rnd_fd.reset(open("/dev/random", O_RDONLY));
+    ASSERT_TRUE(rnd_fd > 0);
+
+    std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+
+    // Fill random data
+    for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+                  true);
+
+        offset += 1_MiB;
+    }
+
+    CowOptions options;
+    options.compression = "gz";
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+
+    size_t num_blocks = size_ / options.block_size;
+    size_t blk_end_copy = num_blocks * 3;
+    size_t source_blk = num_blocks - 1;
+    size_t blk_src_copy = blk_end_copy - 1;
+    uint16_t xor_offset = 5;
+
+    size_t x = num_blocks;
+    while (1) {
+        ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+        x -= 1;
+        if (x == 0) {
+            break;
+        }
+        source_blk -= 1;
+        blk_src_copy -= 1;
+    }
+
+    for (size_t i = num_blocks; i > 0; i--) {
+        ASSERT_TRUE(writer.AddXorBlocks(num_blocks + i - 1,
+                                        &random_buffer_1_.get()[options.block_size * (i - 1)],
+                                        options.block_size, 2 * num_blocks + i - 1, xor_offset));
+    }
+    // Flush operations
+    ASSERT_TRUE(writer.Finalize());
+    // Construct the buffer required for validation
+    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
+    // Read the entire base device
+    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
+              true);
+    // Merged Buffer
+    memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + 2 * size_, size_);
+    memmove(orig_buffer_.get() + size_, (char*)orig_buffer_.get() + 2 * size_ + xor_offset, size_);
+    for (int i = 0; i < size_; i++) {
+        orig_buffer_.get()[size_ + i] ^= random_buffer_1_.get()[i];
+    }
+}
+
+void CowSnapuserdTest::CreateCowDeviceOrderedOps() {
+    unique_fd rnd_fd;
+    loff_t offset = 0;
+
+    std::string path = android::base::GetExecutableDirectory();
+    cow_system_ = std::make_unique<TemporaryFile>(path);
+
+    rnd_fd.reset(open("/dev/random", O_RDONLY));
+    ASSERT_TRUE(rnd_fd > 0);
+
+    std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+
+    // Fill random data
+    for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+                  true);
+
+        offset += 1_MiB;
+    }
+    memset(random_buffer_1_.get(), 0, size_);
+
+    CowOptions options;
+    options.compression = "gz";
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+
+    size_t num_blocks = size_ / options.block_size;
+    size_t x = num_blocks;
+    size_t source_blk = 0;
+    size_t blk_src_copy = 2 * num_blocks;
+    uint16_t xor_offset = 5;
+
+    while (1) {
+        ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+
+        x -= 1;
+        if (x == 0) {
+            break;
+        }
+        source_blk += 1;
+        blk_src_copy += 1;
+    }
+
+    ASSERT_TRUE(writer.AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
+                                    xor_offset));
+    // Flush operations
+    ASSERT_TRUE(writer.Finalize());
+    // Construct the buffer required for validation
+    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
+    // Read the entire base device
+    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
+              true);
+    // Merged Buffer
+    memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + 2 * size_, size_);
+    memmove(orig_buffer_.get() + size_, (char*)orig_buffer_.get() + 2 * size_ + xor_offset, size_);
+    for (int i = 0; i < size_; i++) {
+        orig_buffer_.get()[size_ + i] ^= random_buffer_1_.get()[i];
+    }
+}
+
 void CowSnapuserdTest::CreateCowDevice() {
     unique_fd rnd_fd;
     loff_t offset = 0;
@@ -414,6 +561,17 @@
     size_t source_blk = num_blocks - 1;
     size_t blk_src_copy = blk_end_copy - 1;
 
+    uint32_t sequence[num_blocks * 2];
+    // Sequence for Copy ops
+    for (int i = 0; i < num_blocks; i++) {
+        sequence[i] = num_blocks - 1 - i;
+    }
+    // Sequence for Xor ops
+    for (int i = 0; i < num_blocks; i++) {
+        sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
+    }
+    ASSERT_TRUE(writer.AddSequenceData(2 * num_blocks, sequence));
+
     size_t x = num_blocks;
     while (1) {
         ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
@@ -439,6 +597,11 @@
 
     ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
 
+    size_t blk_xor_start = blk_random2_replace_start + num_blocks;
+    size_t xor_offset = BLOCK_SZ / 2;
+    ASSERT_TRUE(writer.AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
+                                    xor_offset));
+
     // Flush operations
     ASSERT_TRUE(writer.Finalize());
     // Construct the buffer required for validation
@@ -448,6 +611,13 @@
     memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
     memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
     memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
+    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_,
+                                               size_ + xor_offset),
+              true);
+    for (int i = 0; i < size_; i++) {
+        orig_buffer_.get()[(size_ * 4) + i] =
+                (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]);
+    }
 }
 
 void CowSnapuserdTest::InitCowDevice() {
@@ -597,6 +767,7 @@
 
 void CowSnapuserdTest::SimulateDaemonRestart() {
     Shutdown();
+    std::this_thread::sleep_for(500ms);
     SetDeviceControlName();
     StartSnapuserdDaemon();
     InitCowDevice();
@@ -605,6 +776,34 @@
     CreateSnapshotDevice();
 }
 
+void CowSnapuserdTest::MergeInterruptRandomly(int max_duration) {
+    std::srand(std::time(nullptr));
+    StartMerge();
+
+    for (int i = 0; i < 20; i++) {
+        int duration = std::rand() % max_duration;
+        std::this_thread::sleep_for(std::chrono::milliseconds(duration));
+        SimulateDaemonRestart();
+        StartMerge();
+    }
+
+    SimulateDaemonRestart();
+    ASSERT_TRUE(Merge());
+}
+
+void CowSnapuserdTest::MergeInterruptFixed(int duration) {
+    StartMerge();
+
+    for (int i = 0; i < 25; i++) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(duration));
+        SimulateDaemonRestart();
+        StartMerge();
+    }
+
+    SimulateDaemonRestart();
+    ASSERT_TRUE(Merge());
+}
+
 void CowSnapuserdTest::MergeInterrupt() {
     // Interrupt merge at various intervals
     StartMerge();
@@ -669,10 +868,9 @@
     void* buffer = snapuserd_->GetExceptionBuffer(1);
     loff_t offset = 0;
     struct disk_exception* de;
-    for (int i = 0; i < 12; i++) {
+    for (int i = 11; i >= 0; i--) {
         de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
         ASSERT_EQ(de->old_chunk, i);
-        ASSERT_EQ(de->new_chunk, new_chunk);
         offset += sizeof(struct disk_exception);
         new_chunk += 1;
     }
@@ -811,71 +1009,71 @@
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 100);
-            ASSERT_EQ(de->new_chunk, 522);
+            ASSERT_EQ(de->new_chunk, 521);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 105);
-            ASSERT_EQ(de->new_chunk, 524);
+            ASSERT_EQ(de->new_chunk, 522);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 110);
-            ASSERT_EQ(de->new_chunk, 526);
+            ASSERT_EQ(de->new_chunk, 523);
             offset += sizeof(struct disk_exception);
 
             // The next 4 operations are batch merged as
             // both old and new chunk are contiguous
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
-            ASSERT_EQ(de->old_chunk, 50);
-            ASSERT_EQ(de->new_chunk, 528);
-            offset += sizeof(struct disk_exception);
-
-            de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
-            ASSERT_EQ(de->old_chunk, 51);
-            ASSERT_EQ(de->new_chunk, 529);
+            ASSERT_EQ(de->old_chunk, 53);
+            ASSERT_EQ(de->new_chunk, 524);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 52);
-            ASSERT_EQ(de->new_chunk, 530);
+            ASSERT_EQ(de->new_chunk, 525);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
-            ASSERT_EQ(de->old_chunk, 53);
-            ASSERT_EQ(de->new_chunk, 531);
+            ASSERT_EQ(de->old_chunk, 51);
+            ASSERT_EQ(de->new_chunk, 526);
+            offset += sizeof(struct disk_exception);
+
+            de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
+            ASSERT_EQ(de->old_chunk, 50);
+            ASSERT_EQ(de->new_chunk, 527);
             offset += sizeof(struct disk_exception);
 
             // This is handling overlap operation with
             // two batch merge operations.
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 18);
-            ASSERT_EQ(de->new_chunk, 533);
+            ASSERT_EQ(de->new_chunk, 528);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 19);
-            ASSERT_EQ(de->new_chunk, 534);
+            ASSERT_EQ(de->new_chunk, 529);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 20);
-            ASSERT_EQ(de->new_chunk, 535);
+            ASSERT_EQ(de->new_chunk, 530);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 21);
-            ASSERT_EQ(de->new_chunk, 537);
+            ASSERT_EQ(de->new_chunk, 532);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 22);
-            ASSERT_EQ(de->new_chunk, 538);
+            ASSERT_EQ(de->new_chunk, 533);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 23);
-            ASSERT_EQ(de->new_chunk, 539);
+            ASSERT_EQ(de->new_chunk, 534);
             offset += sizeof(struct disk_exception);
 
             // End of metadata
@@ -887,6 +1085,35 @@
     }
 }
 
+TEST(Snapuserd_Test, xor_buffer) {
+    std::string data = "Test String";
+    std::string jumbled = {0x0C, 0x2A, 0x21, 0x54, 0x73, 0x27, 0x06, 0x1B, 0x07, 0x09, 0x46};
+    std::string result = "XOR String!";
+
+    BufferSink sink;
+    XorSink xor_sink;
+    sink.Initialize(sizeof(struct dm_user_header) + 10);
+    int buffsize = 5;
+    xor_sink.Initialize(&sink, buffsize);
+
+    void* buff = sink.GetPayloadBuffer(data.length());
+    memcpy(buff, data.data(), data.length());
+
+    size_t actual;
+    size_t count = 0;
+    while (count < data.length()) {
+        void* xor_buff = xor_sink.GetBuffer(10, &actual);
+        ASSERT_EQ(actual, buffsize);
+        ASSERT_NE(xor_buff, nullptr);
+        memcpy(xor_buff, jumbled.data() + count, buffsize);
+        xor_sink.ReturnData(xor_buff, actual);
+        count += actual;
+    }
+
+    std::string answer = reinterpret_cast<char*>(sink.GetPayloadBufPtr());
+    ASSERT_EQ(answer, result);
+}
+
 TEST(Snapuserd_Test, Snapshot_Metadata) {
     CowSnapuserdMetadataTest harness;
     harness.Setup();
@@ -945,6 +1172,38 @@
     harness.ReadDmUserBlockWithoutDaemon();
 }
 
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Ordered) {
+    CowSnapuserdTest harness;
+    ASSERT_TRUE(harness.SetupOrderedOps());
+    harness.MergeInterruptFixed(300);
+    harness.ValidateMerge();
+    harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Ordered) {
+    CowSnapuserdTest harness;
+    ASSERT_TRUE(harness.SetupOrderedOps());
+    harness.MergeInterruptRandomly(500);
+    harness.ValidateMerge();
+    harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Inverted) {
+    CowSnapuserdTest harness;
+    ASSERT_TRUE(harness.SetupOrderedOpsInverted());
+    harness.MergeInterruptFixed(50);
+    harness.ValidateMerge();
+    harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Inverted) {
+    CowSnapuserdTest harness;
+    ASSERT_TRUE(harness.SetupOrderedOpsInverted());
+    harness.MergeInterruptRandomly(50);
+    harness.ValidateMerge();
+    harness.Shutdown();
+}
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
similarity index 92%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
rename to fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index 280e857..aeecf41 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
@@ -31,6 +31,7 @@
 static constexpr uint32_t PACKET_SIZE = 512;
 
 static constexpr char kSnapuserdSocket[] = "snapuserd";
+static constexpr char kSnapuserdSocketProxy[] = "snapuserd_proxy";
 
 // Ensure that the second-stage daemon for snapuserd is running.
 bool EnsureSnapuserdStarted();
@@ -75,6 +76,9 @@
     // snapuserd to gracefully exit once all handler threads have terminated.
     // This should only be used on first-stage instances of snapuserd.
     bool DetachSnapuserd();
+
+    // Returns true if the snapuserd instance supports bridging a socket to second-stage init.
+    bool SupportsSecondStageSocketHandoff();
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
similarity index 97%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
rename to fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
index 6bb7a39..c592257 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
@@ -47,8 +47,6 @@
 static constexpr uint32_t CHUNK_SIZE = 8;
 static constexpr uint32_t CHUNK_SHIFT = (__builtin_ffs(CHUNK_SIZE) - 1);
 
-#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
-
 // This structure represents the kernel COW header.
 // All the below fields should be in Little Endian format.
 struct disk_header {
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
similarity index 78%
rename from fs_mgr/libsnapshot/snapuserd.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
index a09b111..2abf431 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
@@ -16,11 +16,23 @@
 
 #include "snapuserd.h"
 
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <unistd.h>
+#include <algorithm>
+
 #include <csignal>
 #include <optional>
 #include <set>
 
-#include <libsnapshot/snapuserd_client.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <snapuserd/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
@@ -53,6 +65,10 @@
     return true;
 }
 
+std::unique_ptr<CowReader> Snapuserd::CloneReaderForWorker() {
+    return reader_->CloneCowReader();
+}
+
 bool Snapuserd::CommitMerge(int num_merge_ops) {
     struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
     ch->num_merge_ops += num_merge_ops;
@@ -334,7 +350,7 @@
     CowHeader header;
     CowOptions options;
     bool metadata_found = false;
-    int replace_ops = 0, zero_ops = 0, copy_ops = 0;
+    int replace_ops = 0, zero_ops = 0, copy_ops = 0, xor_ops = 0;
 
     SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
 
@@ -405,7 +421,6 @@
         de->old_chunk = cow_op->new_block;
         de->new_chunk = data_chunk_id;
 
-
         // Store operation pointer.
         chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
         num_ops += 1;
@@ -437,14 +452,15 @@
 
     int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
     std::optional<chunk_t> prev_id = {};
-    std::map<uint64_t, const CowOperation*> map;
+    std::vector<const CowOperation*> vec;
     std::set<uint64_t> dest_blocks;
-    size_t pending_copy_ops = exceptions_per_area_ - num_ops;
-    uint64_t total_copy_ops = reader_->get_num_ordered_ops_to_merge();
+    std::set<uint64_t> source_blocks;
+    size_t pending_ordered_ops = exceptions_per_area_ - num_ops;
+    uint64_t total_ordered_ops = reader_->get_num_ordered_ops_to_merge();
 
     SNAP_LOG(DEBUG) << " Processing copy-ops at Area: " << vec_.size()
                     << " Number of replace/zero ops completed in this area: " << num_ops
-                    << " Pending copy ops for this area: " << pending_copy_ops;
+                    << " Pending copy ops for this area: " << pending_ordered_ops;
 
     while (!cowop_rm_iter->Done()) {
         do {
@@ -492,103 +508,64 @@
             // scratch space and re-construct it thereby there
             // is no loss of data.
             //
+            // Note that we will follow the same order of COW operations
+            // as present in the COW file. This will make sure that
+            // the merge of operations are done based on the ops present
+            // in the file.
             //===========================================================
-            //
-            // Case 2:
-            //
-            // Let's say we have three copy operations written to COW file
-            // in the following order:
-            //
-            // op-1: 15 -> 18
-            // op-2: 16 -> 19
-            // op-3: 17 -> 20
-            //
-            // As aforementioned, kernel will initiate merge in reverse order.
-            // Hence, we will read these ops in reverse order so that all these
-            // ops are exectued in the same order as requested. Thus, we will
-            // read the metadata in reverse order and for the kernel it will
-            // look like:
-            //
-            // op-3: 17 -> 20
-            // op-2: 16 -> 19
-            // op-1: 15 -> 18   <-- Merge starts here in the kernel
-            //
-            // Now, this is problematic as kernel cannot batch merge them.
-            //
-            // Merge sequence will look like:
-            //
-            // Merge-1: op-1: 15 -> 18
-            // Merge-2: op-2: 16 -> 19
-            // Merge-3: op-3: 17 -> 20
-            //
-            // We have three merge operations.
-            //
-            // Even though the blocks are contiguous, kernel can batch merge
-            // them if the blocks are in descending order. Update engine
-            // addresses this issue partially for overlapping operations as
-            // we see that op-1 to op-3 and op-4 to op-6 operatiosn are in
-            // descending order. However, if the copy operations are not
-            // overlapping, update engine cannot write these blocks
-            // in descending order. Hence, we will try to address it.
-            // Thus, we will send these blocks to the kernel and it will
-            // look like:
-            //
-            // op-3: 15 -> 18
-            // op-2: 16 -> 19
-            // op-1: 17 -> 20  <-- Merge starts here in the kernel
-            //
-            // Now with this change, we can batch merge all these three
-            // operations. Merge sequence will look like:
-            //
-            // Merge-1: {op-1: 17 -> 20, op-2: 16 -> 19, op-3: 15 -> 18}
-            //
-            // Note that we have changed the ordering of merge; However, this
-            // is ok as each of these copy operations are independent and there
-            // is no overlap.
-            //
-            //===================================================================
+            uint64_t block_source = cow_op->source;
+            uint64_t block_offset = 0;
+            if (cow_op->type == kCowXorOp) {
+                block_source /= BLOCK_SZ;
+                block_offset = cow_op->source % BLOCK_SZ;
+            }
             if (prev_id.has_value()) {
-                chunk_t diff = (cow_op->new_block > prev_id.value())
-                                       ? (cow_op->new_block - prev_id.value())
-                                       : (prev_id.value() - cow_op->new_block);
-                if (diff != 1) {
-                    break;
-                }
-
-                if (dest_blocks.count(cow_op->new_block) || map.count(cow_op->source) > 0) {
+                if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source) ||
+                    (block_offset > 0 && source_blocks.count(block_source + 1))) {
                     break;
                 }
             }
             metadata_found = true;
-            pending_copy_ops -= 1;
-            map[cow_op->new_block] = cow_op;
-            dest_blocks.insert(cow_op->source);
+            pending_ordered_ops -= 1;
+            vec.push_back(cow_op);
+            dest_blocks.insert(block_source);
+            if (block_offset > 0) {
+                dest_blocks.insert(block_source + 1);
+            }
+            source_blocks.insert(cow_op->new_block);
             prev_id = cow_op->new_block;
             cowop_rm_iter->Next();
-        } while (!cowop_rm_iter->Done() && pending_copy_ops);
+        } while (!cowop_rm_iter->Done() && pending_ordered_ops);
 
         data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
-        SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << map.size()
+        SNAP_LOG(DEBUG) << "Batch Merge copy-ops/xor-ops of size: " << vec.size()
                         << " Area: " << vec_.size() << " Area offset: " << offset
-                        << " Pending-copy-ops in this area: " << pending_copy_ops;
+                        << " Pending-ordered-ops in this area: " << pending_ordered_ops;
 
-        for (auto it = map.begin(); it != map.end(); it++) {
+        for (size_t i = 0; i < vec.size(); i++) {
             struct disk_exception* de =
                     reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
-            de->old_chunk = it->first;
+            const CowOperation* cow_op = vec[i];
+
+            de->old_chunk = cow_op->new_block;
             de->new_chunk = data_chunk_id;
 
             // Store operation pointer.
-            chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), it->second));
+            chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
             offset += sizeof(struct disk_exception);
             num_ops += 1;
-            copy_ops++;
+            if (cow_op->type == kCowCopyOp) {
+                copy_ops++;
+            } else {  // it->second->type == kCowXorOp
+                xor_ops++;
+            }
+
             if (read_ahead_feature_) {
-                read_ahead_ops_.push_back(it->second);
+                read_ahead_ops_.push_back(cow_op);
             }
 
             SNAP_LOG(DEBUG) << num_ops << ":"
-                            << " Copy-op: "
+                            << " Ordered-op: "
                             << " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
 
             if (num_ops == exceptions_per_area_) {
@@ -608,27 +585,28 @@
                     SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
                 }
 
-                if (!(pending_copy_ops == 0)) {
-                    SNAP_LOG(ERROR)
-                            << "Invalid pending_copy_ops: expected: 0 found: " << pending_copy_ops;
+                if (!(pending_ordered_ops == 0)) {
+                    SNAP_LOG(ERROR) << "Invalid pending_ordered_ops: expected: 0 found: "
+                                    << pending_ordered_ops;
                     return false;
                 }
-                pending_copy_ops = exceptions_per_area_;
+                pending_ordered_ops = exceptions_per_area_;
             }
 
             data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
-            total_copy_ops -= 1;
+            total_ordered_ops -= 1;
             /*
              * Split the number of ops based on the size of read-ahead buffer
              * region. We need to ensure that kernel doesn't issue IO on blocks
              * which are not read by the read-ahead thread.
              */
-            if (read_ahead_feature_ && (total_copy_ops % num_ra_ops_per_iter == 0)) {
+            if (read_ahead_feature_ && (total_ordered_ops % num_ra_ops_per_iter == 0)) {
                 data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
             }
         }
-        map.clear();
+        vec.clear();
         dest_blocks.clear();
+        source_blocks.clear();
         prev_id.reset();
     }
 
@@ -651,8 +629,8 @@
     SNAP_LOG(INFO) << "ReadMetadata completed. Final-chunk-id: " << data_chunk_id
                    << " Num Sector: " << ChunkToSector(data_chunk_id)
                    << " Replace-ops: " << replace_ops << " Zero-ops: " << zero_ops
-                   << " Copy-ops: " << copy_ops << " Areas: " << vec_.size()
-                   << " Num-ops-merged: " << header.num_merge_ops
+                   << " Copy-ops: " << copy_ops << " Xor-ops: " << xor_ops
+                   << " Areas: " << vec_.size() << " Num-ops-merged: " << header.num_merge_ops
                    << " Total-data-ops: " << reader_->get_num_total_data_ops();
 
     // Total number of sectors required for creating dm-user device
@@ -712,6 +690,74 @@
     return ReadMetadata();
 }
 
+void Snapuserd::ReadBlocksToCache(const std::string& dm_block_device,
+                                  const std::string partition_name, off_t offset, size_t size) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
+    if (fd.get() == -1) {
+        SNAP_PLOG(ERROR) << "Error reading " << dm_block_device
+                         << " partition-name: " << partition_name;
+        return;
+    }
+
+    size_t remain = size;
+    off_t file_offset = offset;
+    // We pick 4M I/O size based on the fact that the current
+    // update_verifier has a similar I/O size.
+    size_t read_sz = 1024 * BLOCK_SZ;
+    std::vector<uint8_t> buf(read_sz);
+
+    while (remain > 0) {
+        size_t to_read = std::min(remain, read_sz);
+
+        if (!android::base::ReadFullyAtOffset(fd.get(), buf.data(), to_read, file_offset)) {
+            SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
+                             << " at offset: " << file_offset
+                             << " partition-name: " << partition_name << " total-size: " << size
+                             << " remain_size: " << remain;
+            return;
+        }
+
+        file_offset += to_read;
+        remain -= to_read;
+    }
+
+    SNAP_LOG(INFO) << "Finished reading block-device: " << dm_block_device
+                   << " partition: " << partition_name << " size: " << size
+                   << " offset: " << offset;
+}
+
+void Snapuserd::ReadBlocks(const std::string partition_name, const std::string& dm_block_device) {
+    SNAP_LOG(DEBUG) << "Reading partition: " << partition_name
+                    << " Block-Device: " << dm_block_device;
+
+    uint64_t dev_sz = 0;
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        SNAP_LOG(ERROR) << "Cannot open block device";
+        return;
+    }
+
+    dev_sz = get_block_device_size(fd.get());
+    if (!dev_sz) {
+        SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
+        return;
+    }
+
+    int num_threads = 2;
+    size_t num_blocks = dev_sz >> BLOCK_SHIFT;
+    size_t num_blocks_per_thread = num_blocks / num_threads;
+    size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
+    off_t offset = 0;
+
+    for (int i = 0; i < num_threads; i++) {
+        std::async(std::launch::async, &Snapuserd::ReadBlocksToCache, this, dm_block_device,
+                   partition_name, offset, read_sz_per_thread);
+
+        offset += read_sz_per_thread;
+    }
+}
+
 /*
  * Entry point to launch threads
  */
@@ -740,6 +786,39 @@
                 std::async(std::launch::async, &WorkerThread::RunThread, worker_threads_[i].get()));
     }
 
+    bool second_stage_init = true;
+
+    // We don't want to read the blocks during first stage init.
+    if (android::base::EndsWith(misc_name_, "-init") || is_socket_present_) {
+        second_stage_init = false;
+    }
+
+    if (second_stage_init) {
+        SNAP_LOG(INFO) << "Reading blocks to cache....";
+        auto& dm = DeviceMapper::Instance();
+        auto dm_block_devices = dm.FindDmPartitions();
+        if (dm_block_devices.empty()) {
+            SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
+        } else {
+            auto parts = android::base::Split(misc_name_, "-");
+            std::string partition_name = parts[0];
+
+            const char* suffix_b = "_b";
+            const char* suffix_a = "_a";
+
+            partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
+            partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
+
+            if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
+                SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
+            } else {
+                ReadBlocks(partition_name, dm_block_devices.at(partition_name));
+            }
+        }
+    } else {
+        SNAP_LOG(INFO) << "Not reading block device into cache";
+    }
+
     bool ret = true;
     for (auto& t : threads) {
         ret = t.get() && ret;
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
similarity index 90%
rename from fs_mgr/libsnapshot/snapuserd.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd.h
index 5d86e4f..b30041d 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
@@ -38,10 +38,11 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
 #include <libdm/dm.h>
 #include <libsnapshot/cow_reader.h>
 #include <libsnapshot/cow_writer.h>
-#include <libsnapshot/snapuserd_kernel.h>
+#include <snapuserd/snapuserd_kernel.h>
 
 namespace android {
 namespace snapshot {
@@ -107,6 +108,20 @@
     size_t buffer_size_;
 };
 
+class XorSink : public IByteSink {
+  public:
+    void Initialize(BufferSink* sink, size_t size);
+    void Reset();
+    void* GetBuffer(size_t requested, size_t* actual) override;
+    bool ReturnData(void* buffer, size_t len) override;
+
+  private:
+    BufferSink* bufsink_;
+    std::unique_ptr<uint8_t[]> buffer_;
+    size_t buffer_size_;
+    size_t returned_;
+};
+
 class Snapuserd;
 
 class ReadAheadThread {
@@ -116,10 +131,10 @@
     bool RunThread();
 
   private:
-    void InitializeIter();
-    bool IterDone();
-    void IterNext();
-    const CowOperation* GetIterOp();
+    void InitializeRAIter();
+    bool RAIterDone();
+    void RAIterNext();
+    const CowOperation* GetRAOpIter();
     void InitializeBuffer();
 
     bool InitializeFds();
@@ -129,7 +144,7 @@
     }
 
     bool ReadAheadIOStart();
-    void PrepareReadAhead(uint64_t* source_block, int* pending_ops, std::vector<uint64_t>& blocks);
+    void PrepareReadAhead(uint64_t* source_offset, int* pending_ops, std::vector<uint64_t>& blocks);
     bool ReconstructDataFromCow();
     void CheckOverlap(const CowOperation* cow_op);
 
@@ -187,7 +202,9 @@
     // Processing COW operations
     bool ProcessCowOp(const CowOperation* cow_op);
     bool ProcessReplaceOp(const CowOperation* cow_op);
+    // Handles Copy and Xor
     bool ProcessCopyOp(const CowOperation* cow_op);
+    bool ProcessXorOp(const CowOperation* cow_op);
     bool ProcessZeroOp();
 
     bool ReadFromBaseDevice(const CowOperation* cow_op);
@@ -206,6 +223,7 @@
 
     std::unique_ptr<CowReader> reader_;
     BufferSink bufsink_;
+    XorSink xorsink_;
 
     std::string cow_device_;
     std::string backing_store_device_;
@@ -244,6 +262,7 @@
     void* GetExceptionBuffer(size_t i) { return vec_[i].get(); }
 
     bool InitializeWorkers();
+    std::unique_ptr<CowReader> CloneReaderForWorker();
     std::shared_ptr<Snapuserd> GetSharedPtr() { return shared_from_this(); }
 
     std::vector<std::pair<sector_t, const CowOperation*>>& GetChunkVec() { return chunk_vec_; }
@@ -284,6 +303,7 @@
     // Total number of blocks to be merged in a given read-ahead buffer region
     void SetTotalRaBlocksMerged(int x) { total_ra_blocks_merged_ = x; }
     int GetTotalRaBlocksMerged() { return total_ra_blocks_merged_; }
+    void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
 
   private:
     bool IsChunkIdMetadata(chunk_t chunk);
@@ -296,6 +316,10 @@
     bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
     struct BufferState* GetBufferState();
 
+    void ReadBlocks(const std::string partition_name, const std::string& dm_block_device);
+    void ReadBlocksToCache(const std::string& dm_block_device, const std::string partition_name,
+                           off_t offset, size_t size);
+
     std::string cow_device_;
     std::string backing_store_device_;
     std::string control_device_;
@@ -335,6 +359,7 @@
 
     bool merge_initiated_ = false;
     bool attached_ = false;
+    bool is_socket_present_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
new file mode 100644
index 0000000..2750096
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
@@ -0,0 +1,19 @@
+service snapuserd /system/bin/snapuserd
+    socket snapuserd stream 0660 system system
+    oneshot
+    disabled
+    user root
+    group root system
+    seclabel u:r:snapuserd:s0
+
+service snapuserd_proxy /system/bin/snapuserd -socket-handoff
+    socket snapuserd stream 0660 system system
+    socket snapuserd_proxy seqpacket 0660 system root
+    oneshot
+    disabled
+    user root
+    group root system
+    seclabel u:r:snapuserd:s0
+
+on property:init.svc.snapuserd=stopped
+    setprop snapuserd.ready false
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
similarity index 88%
rename from fs_mgr/libsnapshot/snapuserd_client.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 41ab344..1ea05a3 100644
--- a/fs_mgr/libsnapshot/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -33,7 +33,7 @@
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
@@ -42,13 +42,15 @@
 using android::base::unique_fd;
 
 bool EnsureSnapuserdStarted() {
-    if (android::base::GetProperty("init.svc.snapuserd", "") == "running") {
-        return true;
+    if (android::base::GetProperty("init.svc.snapuserd", "") != "running") {
+        android::base::SetProperty("ctl.start", "snapuserd");
+        if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) {
+            LOG(ERROR) << "Timed out waiting for snapuserd to start.";
+            return false;
+        }
     }
-
-    android::base::SetProperty("ctl.start", "snapuserd");
-    if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) {
-        LOG(ERROR) << "Timed out waiting for snapuserd to start.";
+    if (!android::base::WaitForProperty("snapuserd.ready", "true", 10s)) {
+        LOG(ERROR) << "Timed out waiting for snapuserd to be ready.";
         return false;
     }
     return true;
@@ -141,6 +143,16 @@
     return true;
 }
 
+bool SnapuserdClient::SupportsSecondStageSocketHandoff() {
+    std::string msg = "supports,second_stage_socket_handoff";
+    if (!Sendmsg(msg)) {
+        LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
+        return false;
+    }
+    std::string response = Receivemsg();
+    return response == "success";
+}
+
 std::string SnapuserdClient::Receivemsg() {
     char msg[PACKET_SIZE];
     ssize_t ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, sizeof(msg), 0));
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
similarity index 82%
rename from fs_mgr/libsnapshot/snapuserd_daemon.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index 7fa01b7..e05822e 100644
--- a/fs_mgr/libsnapshot/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -19,13 +19,15 @@
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <gflags/gflags.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 
 #include "snapuserd_server.h"
 
 DEFINE_string(socket, android::snapshot::kSnapuserdSocket, "Named socket or socket path.");
 DEFINE_bool(no_socket, false,
             "If true, no socket is used. Each additional argument is an INIT message.");
+DEFINE_bool(socket_handoff, false,
+            "If true, perform a socket hand-off with an existing snapuserd instance, then exit.");
 
 namespace android {
 namespace snapshot {
@@ -33,8 +35,28 @@
 bool Daemon::StartServer(int argc, char** argv) {
     int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, true);
 
+    sigfillset(&signal_mask_);
+    sigdelset(&signal_mask_, SIGINT);
+    sigdelset(&signal_mask_, SIGTERM);
+    sigdelset(&signal_mask_, SIGUSR1);
+
+    // Masking signals here ensure that after this point, we won't handle INT/TERM
+    // until after we call into ppoll()
+    signal(SIGINT, Daemon::SignalHandler);
+    signal(SIGTERM, Daemon::SignalHandler);
+    signal(SIGPIPE, Daemon::SignalHandler);
+    signal(SIGUSR1, Daemon::SignalHandler);
+
+    MaskAllSignalsExceptIntAndTerm();
+
+    if (FLAGS_socket_handoff) {
+        return server_.RunForSocketHandoff();
+    }
     if (!FLAGS_no_socket) {
-        return server_.Start(FLAGS_socket);
+        if (!server_.Start(FLAGS_socket)) {
+            return false;
+        }
+        return server_.Run();
     }
 
     for (int i = arg_start; i < argc; i++) {
@@ -51,8 +73,7 @@
 
     // Skip the accept() call to avoid spurious log spam. The server will still
     // run until all handlers have completed.
-    server_.SetTerminating();
-    return true;
+    return server_.WaitForSocket();
 }
 
 void Daemon::MaskAllSignalsExceptIntAndTerm() {
@@ -61,6 +82,7 @@
     sigdelset(&signal_mask, SIGINT);
     sigdelset(&signal_mask, SIGTERM);
     sigdelset(&signal_mask, SIGPIPE);
+    sigdelset(&signal_mask, SIGUSR1);
     if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) {
         PLOG(ERROR) << "Failed to set sigprocmask";
     }
@@ -74,28 +96,14 @@
     }
 }
 
-void Daemon::Run() {
-    sigfillset(&signal_mask_);
-    sigdelset(&signal_mask_, SIGINT);
-    sigdelset(&signal_mask_, SIGTERM);
-
-    // Masking signals here ensure that after this point, we won't handle INT/TERM
-    // until after we call into ppoll()
-    signal(SIGINT, Daemon::SignalHandler);
-    signal(SIGTERM, Daemon::SignalHandler);
-    signal(SIGPIPE, Daemon::SignalHandler);
-
-    LOG(DEBUG) << "Snapuserd-server: ready to accept connections";
-
-    MaskAllSignalsExceptIntAndTerm();
-
-    server_.Run();
-}
-
 void Daemon::Interrupt() {
     server_.Interrupt();
 }
 
+void Daemon::ReceivedSocketSignal() {
+    server_.ReceivedSocketSignal();
+}
+
 void Daemon::SignalHandler(int signal) {
     LOG(DEBUG) << "Snapuserd received signal: " << signal;
     switch (signal) {
@@ -108,6 +116,11 @@
             LOG(ERROR) << "Received SIGPIPE signal";
             break;
         }
+        case SIGUSR1: {
+            LOG(INFO) << "Received SIGUSR1, attaching to proxy socket";
+            Daemon::Instance().ReceivedSocketSignal();
+            break;
+        }
         default:
             LOG(ERROR) << "Received unknown signal " << signal;
             break;
@@ -126,7 +139,5 @@
         LOG(ERROR) << "Snapuserd daemon failed to start.";
         exit(EXIT_FAILURE);
     }
-    daemon.Run();
-
     return 0;
 }
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
similarity index 97%
rename from fs_mgr/libsnapshot/snapuserd_daemon.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
index f8afac5..b660ba2 100644
--- a/fs_mgr/libsnapshot/snapuserd_daemon.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
@@ -36,8 +36,8 @@
     }
 
     bool StartServer(int argc, char** argv);
-    void Run();
     void Interrupt();
+    void ReceivedSocketSignal();
 
   private:
     // Signal mask used with ppoll()
diff --git a/fs_mgr/libsnapshot/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
similarity index 86%
rename from fs_mgr/libsnapshot/snapuserd_readahead.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
index 16d5919..b868eed 100644
--- a/fs_mgr/libsnapshot/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
@@ -20,7 +20,7 @@
 #include <optional>
 #include <set>
 
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
@@ -172,24 +172,37 @@
 }
 
 void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
-    if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(cow_op->source)) {
+    uint64_t source_block = cow_op->source;
+    uint64_t source_offset = 0;
+    if (cow_op->type == kCowXorOp) {
+        source_block /= BLOCK_SZ;
+        source_offset = cow_op->source % BLOCK_SZ;
+    }
+    if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block) ||
+        (source_offset > 0 && source_blocks_.count(source_block + 1))) {
         overlap_ = true;
     }
 
-    dest_blocks_.insert(cow_op->source);
+    dest_blocks_.insert(source_block);
+    if (source_offset > 0) {
+        dest_blocks_.insert(source_block + 1);
+    }
     source_blocks_.insert(cow_op->new_block);
 }
 
-void ReadAheadThread::PrepareReadAhead(uint64_t* source_block, int* pending_ops,
+void ReadAheadThread::PrepareReadAhead(uint64_t* source_offset, int* pending_ops,
                                        std::vector<uint64_t>& blocks) {
     int num_ops = *pending_ops;
     int nr_consecutive = 0;
 
-    if (!IterDone() && num_ops) {
-        // Get the first block
-        const CowOperation* cow_op = GetIterOp();
-        *source_block = cow_op->source;
-        IterNext();
+    if (!RAIterDone() && num_ops) {
+        // Get the first block with offset
+        const CowOperation* cow_op = GetRAOpIter();
+        *source_offset = cow_op->source;
+        if (cow_op->type == kCowCopyOp) {
+            *source_offset *= BLOCK_SZ;
+        }
+        RAIterNext();
         num_ops -= 1;
         nr_consecutive = 1;
         blocks.push_back(cow_op->new_block);
@@ -201,15 +214,19 @@
         /*
          * Find number of consecutive blocks working backwards.
          */
-        while (!IterDone() && num_ops) {
-            const CowOperation* op = GetIterOp();
-            if (op->source != (*source_block - nr_consecutive)) {
+        while (!RAIterDone() && num_ops) {
+            const CowOperation* op = GetRAOpIter();
+            uint64_t next_offset = op->source;
+            if (cow_op->type == kCowCopyOp) {
+                next_offset *= BLOCK_SZ;
+            }
+            if (next_offset != (*source_offset - nr_consecutive * BLOCK_SZ)) {
                 break;
             }
             nr_consecutive += 1;
             num_ops -= 1;
             blocks.push_back(op->new_block);
-            IterNext();
+            RAIterNext();
 
             if (!overlap_) {
                 CheckOverlap(op);
@@ -247,12 +264,12 @@
     // We are done re-constructing the mapping; however, we need to make sure
     // all the COW operations to-be merged are present in the re-constructed
     // mapping.
-    while (!IterDone()) {
-        const CowOperation* op = GetIterOp();
+    while (!RAIterDone()) {
+        const CowOperation* op = GetRAOpIter();
         if (read_ahead_buffer_map.find(op->new_block) != read_ahead_buffer_map.end()) {
             num_ops -= 1;
             snapuserd_->SetFinalBlockMerged(op->new_block);
-            IterNext();
+            RAIterNext();
         } else {
             // Verify that we have covered all the ops which were re-constructed
             // from COW device - These are the ops which are being
@@ -312,10 +329,10 @@
     source_blocks_.clear();
 
     while (true) {
-        uint64_t source_block;
+        uint64_t source_offset;
         int linear_blocks;
 
-        PrepareReadAhead(&source_block, &num_ops, blocks);
+        PrepareReadAhead(&source_offset, &num_ops, blocks);
         linear_blocks = blocks.size();
         if (linear_blocks == 0) {
             // No more blocks to read
@@ -324,7 +341,7 @@
         }
 
         // Get the first block in the consecutive set of blocks
-        source_block = source_block + 1 - linear_blocks;
+        source_offset = source_offset - (linear_blocks - 1) * BLOCK_SZ;
         size_t io_size = (linear_blocks * BLOCK_SZ);
         num_ops -= linear_blocks;
         total_blocks_merged += linear_blocks;
@@ -358,10 +375,12 @@
         // Read from the base device consecutive set of blocks in one shot
         if (!android::base::ReadFullyAtOffset(backing_store_fd_,
                                               (char*)read_ahead_buffer_ + buffer_offset, io_size,
-                                              source_block * BLOCK_SZ)) {
-            SNAP_PLOG(ERROR) << "Copy-op failed. Read from backing store: " << backing_store_device_
-                             << "at block :" << source_block << " buffer_offset : " << buffer_offset
-                             << " io_size : " << io_size << " buf-addr : " << read_ahead_buffer_;
+                                              source_offset)) {
+            SNAP_PLOG(ERROR) << "Ordered-op failed. Read from backing store: "
+                             << backing_store_device_ << "at block :" << source_offset / BLOCK_SZ
+                             << " offset :" << source_offset % BLOCK_SZ
+                             << " buffer_offset : " << buffer_offset << " io_size : " << io_size
+                             << " buf-addr : " << read_ahead_buffer_;
 
             snapuserd_->ReadAheadIOFailed();
             return false;
@@ -394,10 +413,10 @@
         return false;
     }
 
-    InitializeIter();
+    InitializeRAIter();
     InitializeBuffer();
 
-    while (!IterDone()) {
+    while (!RAIterDone()) {
         if (!ReadAheadIOStart()) {
             return false;
         }
@@ -433,21 +452,21 @@
     return true;
 }
 
-void ReadAheadThread::InitializeIter() {
+void ReadAheadThread::InitializeRAIter() {
     std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
     read_ahead_iter_ = read_ahead_ops.rbegin();
 }
 
-bool ReadAheadThread::IterDone() {
+bool ReadAheadThread::RAIterDone() {
     std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
     return read_ahead_iter_ == read_ahead_ops.rend();
 }
 
-void ReadAheadThread::IterNext() {
+void ReadAheadThread::RAIterNext() {
     read_ahead_iter_++;
 }
 
-const CowOperation* ReadAheadThread::GetIterOp() {
+const CowOperation* ReadAheadThread::GetRAOpIter() {
     return *read_ahead_iter_;
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
similarity index 72%
rename from fs_mgr/libsnapshot/snapuserd_server.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
index 8339690..2f87557 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
@@ -25,14 +25,26 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/logging.h>
-
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <fs_mgr/file_wait.h>
+#include <snapuserd/snapuserd_client.h>
 #include "snapuserd.h"
 #include "snapuserd_server.h"
 
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
 namespace android {
 namespace snapshot {
 
+using namespace std::string_literals;
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
 DaemonOperations SnapuserdServer::Resolveop(std::string& input) {
     if (input == "init") return DaemonOperations::INIT;
     if (input == "start") return DaemonOperations::START;
@@ -40,6 +52,7 @@
     if (input == "query") return DaemonOperations::QUERY;
     if (input == "delete") return DaemonOperations::DELETE;
     if (input == "detach") return DaemonOperations::DETACH;
+    if (input == "supports") return DaemonOperations::SUPPORTS;
 
     return DaemonOperations::INVALID;
 }
@@ -193,6 +206,16 @@
             terminating_ = true;
             return true;
         }
+        case DaemonOperations::SUPPORTS: {
+            if (out.size() != 2) {
+                LOG(ERROR) << "Malformed supports message, " << out.size() << " parts";
+                return Sendmsg(fd, "fail");
+            }
+            if (out[1] == "second_stage_socket_handoff") {
+                return Sendmsg(fd, "success");
+            }
+            return Sendmsg(fd, "fail");
+        }
         default: {
             LOG(ERROR) << "Received unknown message type from client";
             Sendmsg(fd, "fail");
@@ -204,6 +227,7 @@
 void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> handler) {
     LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
 
+    handler->snapuserd()->SetSocketPresent(is_socket_present_);
     if (!handler->snapuserd()->Start()) {
         LOG(ERROR) << " Failed to launch all worker threads";
     }
@@ -245,28 +269,45 @@
 }
 
 bool SnapuserdServer::Start(const std::string& socketname) {
+    bool start_listening = true;
+
     sockfd_.reset(android_get_control_socket(socketname.c_str()));
-    if (sockfd_ >= 0) {
-        if (listen(sockfd_.get(), 4) < 0) {
-            PLOG(ERROR) << "listen socket failed: " << socketname;
-            return false;
-        }
-    } else {
+    if (sockfd_ < 0) {
         sockfd_.reset(socket_local_server(socketname.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
                                           SOCK_STREAM));
         if (sockfd_ < 0) {
             PLOG(ERROR) << "Failed to create server socket " << socketname;
             return false;
         }
+        start_listening = false;
+    }
+    return StartWithSocket(start_listening);
+}
+
+bool SnapuserdServer::StartWithSocket(bool start_listening) {
+    if (start_listening && listen(sockfd_.get(), 4) < 0) {
+        PLOG(ERROR) << "listen socket failed";
+        return false;
     }
 
-    AddWatchedFd(sockfd_);
+    AddWatchedFd(sockfd_, POLLIN);
+    is_socket_present_ = true;
 
-    LOG(DEBUG) << "Snapuserd server successfully started with socket name " << socketname;
+    // If started in first-stage init, the property service won't be online.
+    if (access("/dev/socket/property_service", F_OK) == 0) {
+        if (!android::base::SetProperty("snapuserd.ready", "true")) {
+            LOG(ERROR) << "Failed to set snapuserd.ready property";
+            return false;
+        }
+    }
+
+    LOG(DEBUG) << "Snapuserd server now accepting connections";
     return true;
 }
 
 bool SnapuserdServer::Run() {
+    LOG(INFO) << "Now listening on snapuserd socket";
+
     while (!IsTerminating()) {
         int rv = TEMP_FAILURE_RETRY(poll(watched_fds_.data(), watched_fds_.size(), -1));
         if (rv < 0) {
@@ -311,10 +352,10 @@
     }
 }
 
-void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd) {
+void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
     struct pollfd p = {};
     p.fd = fd.get();
-    p.events = POLLIN;
+    p.events = events;
     watched_fds_.emplace_back(std::move(p));
 }
 
@@ -325,7 +366,7 @@
         return;
     }
 
-    AddWatchedFd(fd);
+    AddWatchedFd(fd, POLLIN);
 }
 
 bool SnapuserdServer::HandleClient(android::base::borrowed_fd fd, int revents) {
@@ -422,5 +463,97 @@
     return true;
 }
 
+bool SnapuserdServer::WaitForSocket() {
+    auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
+
+    auto socket_path = ANDROID_SOCKET_DIR "/"s + kSnapuserdSocketProxy;
+
+    if (!android::fs_mgr::WaitForFile(socket_path, std::chrono::milliseconds::max())) {
+        LOG(ERROR)
+                << "Failed to wait for proxy socket, second-stage snapuserd will fail to connect";
+        return false;
+    }
+
+    // We must re-initialize property service access, since we launched before
+    // second-stage init.
+    __system_properties_init();
+
+    if (!android::base::WaitForProperty("snapuserd.proxy_ready", "true")) {
+        LOG(ERROR)
+                << "Failed to wait for proxy property, second-stage snapuserd will fail to connect";
+        return false;
+    }
+
+    unique_fd fd(socket_local_client(kSnapuserdSocketProxy, ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                     SOCK_SEQPACKET));
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to connect to socket proxy";
+        return false;
+    }
+
+    char code[1];
+    std::vector<unique_fd> fds;
+    ssize_t rv = android::base::ReceiveFileDescriptorVector(fd, code, sizeof(code), 1, &fds);
+    if (rv < 0) {
+        PLOG(ERROR) << "Failed to receive server socket over proxy";
+        return false;
+    }
+    if (fds.empty()) {
+        LOG(ERROR) << "Expected at least one file descriptor from proxy";
+        return false;
+    }
+
+    // We don't care if the ACK is received.
+    code[0] = 'a';
+    if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL) < 0)) {
+        PLOG(ERROR) << "Failed to send ACK to proxy";
+        return false;
+    }
+
+    sockfd_ = std::move(fds[0]);
+    if (!StartWithSocket(true)) {
+        return false;
+    }
+    return Run();
+}
+
+bool SnapuserdServer::RunForSocketHandoff() {
+    unique_fd proxy_fd(android_get_control_socket(kSnapuserdSocketProxy));
+    if (proxy_fd < 0) {
+        PLOG(FATAL) << "Proxy could not get android control socket " << kSnapuserdSocketProxy;
+    }
+    borrowed_fd server_fd(android_get_control_socket(kSnapuserdSocket));
+    if (server_fd < 0) {
+        PLOG(FATAL) << "Proxy could not get android control socket " << kSnapuserdSocket;
+    }
+
+    if (listen(proxy_fd.get(), 4) < 0) {
+        PLOG(FATAL) << "Proxy listen socket failed";
+    }
+
+    if (!android::base::SetProperty("snapuserd.proxy_ready", "true")) {
+        LOG(FATAL) << "Proxy failed to set ready property";
+    }
+
+    unique_fd client_fd(
+            TEMP_FAILURE_RETRY(accept4(proxy_fd.get(), nullptr, nullptr, SOCK_CLOEXEC)));
+    if (client_fd < 0) {
+        PLOG(FATAL) << "Proxy accept failed";
+    }
+
+    char code[1] = {'a'};
+    std::vector<int> fds = {server_fd.get()};
+    ssize_t rv = android::base::SendFileDescriptorVector(client_fd, code, sizeof(code), fds);
+    if (rv < 0) {
+        PLOG(FATAL) << "Proxy could not send file descriptor to snapuserd";
+    }
+    // Wait for an ACK - results don't matter, we just don't want to risk closing
+    // the proxy socket too early.
+    if (recv(client_fd, code, sizeof(code), 0) < 0) {
+        PLOG(FATAL) << "Proxy could not receive terminating code from snapuserd";
+    }
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
similarity index 92%
rename from fs_mgr/libsnapshot/snapuserd_server.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
index 6699189..3b6ff15 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
@@ -42,6 +42,7 @@
     STOP,
     DELETE,
     DETACH,
+    SUPPORTS,
     INVALID,
 };
 
@@ -93,14 +94,16 @@
   private:
     android::base::unique_fd sockfd_;
     bool terminating_;
+    volatile bool received_socket_signal_ = false;
     std::vector<struct pollfd> watched_fds_;
+    bool is_socket_present_ = false;
 
     std::mutex lock_;
 
     using HandlerList = std::vector<std::shared_ptr<DmUserHandler>>;
     HandlerList dm_users_;
 
-    void AddWatchedFd(android::base::borrowed_fd fd);
+    void AddWatchedFd(android::base::borrowed_fd fd, int events);
     void AcceptClient();
     bool HandleClient(android::base::borrowed_fd fd, int revents);
     bool Recv(android::base::borrowed_fd fd, std::string* data);
@@ -117,6 +120,7 @@
 
     void RunThread(std::shared_ptr<DmUserHandler> handler);
     void JoinAllThreads();
+    bool StartWithSocket(bool start_listening);
 
     // Find a DmUserHandler within a lock.
     HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
@@ -129,6 +133,8 @@
     bool Start(const std::string& socketname);
     bool Run();
     void Interrupt();
+    bool RunForSocketHandoff();
+    bool WaitForSocket();
 
     std::shared_ptr<DmUserHandler> AddHandler(const std::string& misc_name,
                                               const std::string& cow_device_path,
@@ -136,6 +142,7 @@
     bool StartHandler(const std::shared_ptr<DmUserHandler>& handler);
 
     void SetTerminating() { terminating_ = true; }
+    void ReceivedSocketSignal() { received_socket_signal_ = true; }
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
similarity index 92%
rename from fs_mgr/libsnapshot/snapuserd_worker.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
index 682f9da..cdf9fe7 100644
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
@@ -20,7 +20,7 @@
 #include <optional>
 #include <set>
 
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
@@ -71,6 +71,39 @@
     return msg->payload.buf;
 }
 
+void XorSink::Initialize(BufferSink* sink, size_t size) {
+    bufsink_ = sink;
+    buffer_size_ = size;
+    returned_ = 0;
+    buffer_ = std::make_unique<uint8_t[]>(size);
+}
+
+void XorSink::Reset() {
+    returned_ = 0;
+}
+
+void* XorSink::GetBuffer(size_t requested, size_t* actual) {
+    if (requested > buffer_size_) {
+        *actual = buffer_size_;
+    } else {
+        *actual = requested;
+    }
+    return buffer_.get();
+}
+
+bool XorSink::ReturnData(void* buffer, size_t len) {
+    uint8_t* xor_data = reinterpret_cast<uint8_t*>(buffer);
+    uint8_t* buff = reinterpret_cast<uint8_t*>(bufsink_->GetPayloadBuffer(len + returned_));
+    if (buff == nullptr) {
+        return false;
+    }
+    for (size_t i = 0; i < len; i++) {
+        buff[returned_ + i] ^= xor_data[i];
+    }
+    returned_ += len;
+    return true;
+}
+
 WorkerThread::WorkerThread(const std::string& cow_device, const std::string& backing_device,
                            const std::string& control_device, const std::string& misc_name,
                            std::shared_ptr<Snapuserd> snapuserd) {
@@ -105,11 +138,11 @@
 }
 
 bool WorkerThread::InitReader() {
-    reader_ = std::make_unique<CowReader>();
+    reader_ = snapuserd_->CloneReaderForWorker();
+
     if (!reader_->InitForMerge(std::move(cow_fd_))) {
         return false;
     }
-
     return true;
 }
 
@@ -150,10 +183,19 @@
     }
     SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
                     << " Source: " << cow_op->source;
-    if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ,
-                                          cow_op->source * BLOCK_SZ)) {
-        SNAP_PLOG(ERROR) << "Copy-op failed. Read from backing store: " << backing_store_device_
-                         << "at block :" << cow_op->source;
+    uint64_t offset = cow_op->source;
+    if (cow_op->type == kCowCopyOp) {
+        offset *= BLOCK_SZ;
+    }
+    if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
+        std::string op;
+        if (cow_op->type == kCowCopyOp)
+            op = "Copy-op";
+        else {
+            op = "Xor-op";
+        }
+        SNAP_PLOG(ERROR) << op << " failed. Read from backing store: " << backing_store_device_
+                         << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
         return false;
     }
 
@@ -188,6 +230,23 @@
     return true;
 }
 
+bool WorkerThread::ProcessXorOp(const CowOperation* cow_op) {
+    if (!GetReadAheadPopulatedBuffer(cow_op)) {
+        SNAP_LOG(DEBUG) << " GetReadAheadPopulatedBuffer failed..."
+                        << " new_block: " << cow_op->new_block;
+        if (!ReadFromBaseDevice(cow_op)) {
+            return false;
+        }
+    }
+    xorsink_.Reset();
+    if (!reader_->ReadData(*cow_op, &xorsink_)) {
+        SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block;
+        return false;
+    }
+
+    return true;
+}
+
 bool WorkerThread::ProcessZeroOp() {
     // Zero out the entire block
     void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
@@ -219,6 +278,10 @@
             return ProcessCopyOp(cow_op);
         }
 
+        case kCowXorOp: {
+            return ProcessXorOp(cow_op);
+        }
+
         default: {
             SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
         }
@@ -470,10 +533,10 @@
 }
 
 int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
-                                       int unmerged_exceptions, bool* copy_op, bool* commit) {
+                                       int unmerged_exceptions, bool* ordered_op, bool* commit) {
     int merged_ops_cur_iter = 0;
     std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
-    *copy_op = false;
+    *ordered_op = false;
     std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
 
     // Find the operations which are merged in this cycle.
@@ -511,9 +574,9 @@
             }
             const CowOperation* cow_op = it->second;
 
-            if (snapuserd_->IsReadAheadFeaturePresent() && cow_op->type == kCowCopyOp) {
-                *copy_op = true;
-                // Every single copy operation has to come from read-ahead
+            if (snapuserd_->IsReadAheadFeaturePresent() && IsOrderedOp(*cow_op)) {
+                *ordered_op = true;
+                // Every single ordered operation has to come from read-ahead
                 // cache.
                 if (read_ahead_buffer_map.find(cow_op->new_block) == read_ahead_buffer_map.end()) {
                     SNAP_LOG(ERROR)
@@ -557,7 +620,7 @@
 bool WorkerThread::ProcessMergeComplete(chunk_t chunk, void* buffer) {
     uint32_t stride = exceptions_per_area_ + 1;
     const std::vector<std::unique_ptr<uint8_t[]>>& vec = snapuserd_->GetMetadataVec();
-    bool copy_op = false;
+    bool ordered_op = false;
     bool commit = false;
 
     // ChunkID to vector index
@@ -582,7 +645,7 @@
     }
 
     int merged_ops_cur_iter = GetNumberOfMergedOps(buffer, vec[divresult.quot].get(), offset,
-                                                   unmerged_exceptions, &copy_op, &commit);
+                                                   unmerged_exceptions, &ordered_op, &commit);
 
     // There should be at least one operation merged in this cycle
     if (!(merged_ops_cur_iter > 0)) {
@@ -590,7 +653,7 @@
         return false;
     }
 
-    if (copy_op) {
+    if (ordered_op) {
         if (commit) {
             // Push the flushing logic to read-ahead thread so that merge thread
             // can make forward progress. Sync will happen in the background
@@ -819,6 +882,7 @@
 
 bool WorkerThread::RunThread() {
     InitializeBufsink();
+    xorsink_.Initialize(&bufsink_, BLOCK_SZ);
 
     if (!InitializeFds()) {
         return false;
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
index fd7ea04..42abd09 100644
--- a/fs_mgr/libstorage_literals/Android.bp
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -7,6 +7,7 @@
     name: "libstorage_literals_headers",
     host_supported: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     export_include_dirs: ["."],
     target: {
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 95e814b..a7f0c0e 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -43,8 +43,8 @@
         "libhidlbase",
         "android.hardware.gatekeeper@1.0",
         "libgatekeeper_aidl",
-        "android.hardware.security.keymint-V1-ndk_platform",
-        "android.security.authorization-ndk_platform",
+        "android.hardware.security.keymint-V1-ndk",
+        "android.security.authorization-ndk",
     ],
 
     static_libs: ["libscrypt_static"],
diff --git a/init/Android.bp b/init/Android.bp
index 3e8d4e3..a04d2db 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -228,17 +228,19 @@
     stem: "init",
     defaults: ["init_defaults"],
     static_libs: ["libinit"],
-    required: [
-        "e2fsdroid",
-        "init.rc",
-        "mke2fs",
-        "sload_f2fs",
-        "make_f2fs",
-        "ueventd.rc",
-    ],
     srcs: ["main.cpp"],
     symlinks: ["ueventd"],
     target: {
+        platform: {
+            required: [
+                "init.rc",
+                "ueventd.rc",
+                "e2fsdroid",
+                "make_f2fs",
+                "mke2fs",
+                "sload_f2fs",
+            ],
+        },
         recovery: {
             cflags: ["-DRECOVERY"],
             exclude_static_libs: [
@@ -248,6 +250,14 @@
                 "libbinder",
                 "libutils",
             ],
+            required: [
+                "init_recovery.rc",
+                "ueventd.rc.recovery",
+                "e2fsdroid.recovery",
+                "make_f2fs.recovery",
+                "mke2fs.recovery",
+                "sload_f2fs.recovery",
+            ],
         },
     },
     visibility: ["//packages/modules/Virtualization/microdroid"],
diff --git a/init/README.md b/init/README.md
index 4b04628..f447ab2 100644
--- a/init/README.md
+++ b/init/README.md
@@ -277,8 +277,6 @@
   CLD_EXITED or an status other than '0', reboot the system with the target specified in
   _target_. _target_ takes the same format as the parameter to sys.powerctl. This is particularly
   intended to be used with the `exec_start` builtin for any must-have checks during boot.
-  A service being stopped by init (e.g. using the `stop` or `class_reset` commands) is not
-  considered a failure for the purpose of this setting.
 
 `restart_period <seconds>`
 > If a non-oneshot service exits, it will be restarted at its start time plus
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 9c2a7bb..05e00ed 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -87,7 +87,13 @@
 
     auto iter = devices->find(name);
     if (iter == devices->end()) {
-        return ListenerAction::kContinue;
+        auto partition_name = DeviceHandler::GetPartitionNameForDevice(uevent.device_name);
+        if (!partition_name.empty()) {
+            iter = devices->find(partition_name);
+        }
+        if (iter == devices->end()) {
+            return ListenerAction::kContinue;
+        }
     }
 
     LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << name;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 035038f..7633d9c 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1305,20 +1305,8 @@
 
     return {};
 }
-
-static bool IsApexUpdatable() {
-    static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
-    return updatable;
-}
-
 static Result<void> do_update_linker_config(const BuiltinArguments&) {
-    // If APEX is not updatable, then all APEX information are already included in the first
-    // linker config generation, so there is no need to update linker configuration again.
-    if (IsApexUpdatable()) {
-        return GenerateLinkerConfiguration();
-    }
-
-    return {};
+    return GenerateLinkerConfiguration();
 }
 
 static Result<void> parse_apex_configs() {
diff --git a/init/devices.cpp b/init/devices.cpp
index 56c6623..d4a3cb9 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -46,6 +46,7 @@
 using android::base::ReadFileToString;
 using android::base::Readlink;
 using android::base::Realpath;
+using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::Trim;
@@ -187,6 +188,36 @@
     }
 }
 
+std::string DeviceHandler::GetPartitionNameForDevice(const std::string& query_device) {
+    static const auto partition_map = [] {
+        std::vector<std::pair<std::string, std::string>> partition_map;
+        auto parser = [&partition_map](const std::string& key, const std::string& value) {
+            if (key != "androidboot.partition_map") {
+                return;
+            }
+            for (const auto& map : Split(value, ";")) {
+                auto map_pieces = Split(map, ",");
+                if (map_pieces.size() != 2) {
+                    LOG(ERROR) << "Expected a comma separated device,partition mapping, but found '"
+                               << map << "'";
+                    continue;
+                }
+                partition_map.emplace_back(map_pieces[0], map_pieces[1]);
+            }
+        };
+        ImportKernelCmdline(parser);
+        ImportBootconfig(parser);
+        return partition_map;
+    }();
+
+    for (const auto& [device, partition] : partition_map) {
+        if (query_device == device) {
+            return partition;
+        }
+    }
+    return {};
+}
+
 // Given a path that may start with a platform device, find the parent platform device by finding a
 // parent directory with a 'subsystem' symlink that points to the platform bus.
 // If it doesn't start with a platform device, return false
@@ -389,6 +420,10 @@
         // If we don't have a partition name but we are a partition on a boot device, create a
         // symlink of /dev/block/by-name/<device_name> for symmetry.
         links.emplace_back("/dev/block/by-name/" + uevent.device_name);
+        auto partition_name = GetPartitionNameForDevice(uevent.device_name);
+        if (!partition_name.empty()) {
+            links.emplace_back("/dev/block/by-name/" + partition_name);
+        }
     }
 
     auto last_slash = uevent.path.rfind('/');
diff --git a/init/devices.h b/init/devices.h
index d70d746..f9f4d79 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -122,6 +122,12 @@
 
     std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
 
+    // `androidboot.partition_map` allows associating a partition name for a raw block device
+    // through a comma separated and semicolon deliminated list. For example,
+    // `androidboot.partition_map=vdb,metadata;vdc,userdata` maps `vdb` to `metadata` and `vdc` to
+    // `userdata`.
+    static std::string GetPartitionNameForDevice(const std::string& device);
+
   private:
     bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
     std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
diff --git a/init/init.cpp b/init/init.cpp
index a7325ca..bde8e04 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -725,6 +725,40 @@
     }
 }
 
+static Result<void> ConnectEarlyStageSnapuserdAction(const BuiltinArguments& args) {
+    auto pid = GetSnapuserdFirstStagePid();
+    if (!pid) {
+        return {};
+    }
+
+    auto info = GetSnapuserdFirstStageInfo();
+    if (auto iter = std::find(info.begin(), info.end(), "socket"s); iter == info.end()) {
+        // snapuserd does not support socket handoff, so exit early.
+        return {};
+    }
+
+    // Socket handoff is supported.
+    auto svc = ServiceList::GetInstance().FindService("snapuserd");
+    if (!svc) {
+        LOG(FATAL) << "Failed to find snapuserd service entry";
+    }
+
+    svc->SetShutdownCritical();
+    svc->SetStartedInFirstStage(*pid);
+
+    svc = ServiceList::GetInstance().FindService("snapuserd_proxy");
+    if (!svc) {
+        LOG(FATAL) << "Failed find snapuserd_proxy service entry, merge will never initiate";
+    }
+    if (!svc->MarkSocketPersistent("snapuserd")) {
+        LOG(FATAL) << "Could not find snapuserd socket in snapuserd_proxy service entry";
+    }
+    if (auto result = svc->Start(); !result.ok()) {
+        LOG(FATAL) << "Could not start snapuserd_proxy: " << result.error();
+    }
+    return {};
+}
+
 int SecondStageMain(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
@@ -852,6 +886,7 @@
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
+    am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
     am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 2a57808..593e5ae 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -82,110 +82,21 @@
     return updatable;
 }
 
-#ifdef ACTIVATE_FLATTENED_APEX
-
-static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
-    if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
-        return ErrnoError() << "Could not create mount point " << mount_path;
-    }
-    if (mount(path.c_str(), mount_path.c_str(), nullptr, MS_BIND, nullptr) != 0) {
-        return ErrnoError() << "Could not bind mount " << path << " to " << mount_path;
-    }
-    return {};
+static bool IsMicrodroid() {
+    static bool is_microdroid = android::base::GetProperty("ro.hardware", "") == "microdroid";
+    return is_microdroid;
 }
 
-static Result<apex::proto::ApexManifest> GetApexManifest(const std::string& apex_dir) {
-    const std::string manifest_path = apex_dir + "/apex_manifest.pb";
-    std::string content;
-    if (!android::base::ReadFileToString(manifest_path, &content)) {
-        return Error() << "Failed to read manifest file: " << manifest_path;
-    }
-    apex::proto::ApexManifest manifest;
-    if (!manifest.ParseFromString(content)) {
-        return Error() << "Can't parse manifest file: " << manifest_path;
-    }
-    return manifest;
-}
-
-template <typename Fn>
-static Result<void> ActivateFlattenedApexesFrom(const std::string& from_dir,
-                                                const std::string& to_dir, Fn on_activate) {
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(from_dir.c_str()), closedir);
-    if (!dir) {
-        return {};
-    }
-    dirent* entry;
-    std::vector<std::string> entries;
-
-    while ((entry = readdir(dir.get())) != nullptr) {
-        if (entry->d_name[0] == '.') continue;
-        if (entry->d_type == DT_DIR) {
-            entries.push_back(entry->d_name);
-        }
-    }
-
-    std::sort(entries.begin(), entries.end());
-    for (const auto& name : entries) {
-        const std::string apex_path = from_dir + "/" + name;
-        const auto apex_manifest = GetApexManifest(apex_path);
-        if (!apex_manifest.ok()) {
-            LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
-            continue;
-        }
-        const std::string mount_path = to_dir + "/" + apex_manifest->name();
-        if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
-            return result;
-        }
-        on_activate(apex_path, *apex_manifest);
-    }
-    return {};
-}
-
-static bool ActivateFlattenedApexesIfPossible() {
-    if (IsRecoveryMode() || IsApexUpdatable()) {
-        return true;
-    }
-
-    const std::string kApexTop = "/apex";
-    const std::vector<std::string> kBuiltinDirsForApexes = {
-            "/system/apex",
-            "/system_ext/apex",
-            "/product/apex",
-            "/vendor/apex",
-    };
-
-    std::vector<com::android::apex::ApexInfo> apex_infos;
-    auto on_activate = [&](const std::string& apex_path,
-                           const apex::proto::ApexManifest& apex_manifest) {
-        apex_infos.emplace_back(apex_manifest.name(), apex_path, apex_path, apex_manifest.version(),
-                                apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true,
-                                /* lastUpdateMillis= */ 0);
-    };
-
-    for (const auto& dir : kBuiltinDirsForApexes) {
-        if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop, on_activate); !result.ok()) {
-            LOG(ERROR) << result.error();
-            return false;
-        }
-    }
-
-    std::ostringstream oss;
-    com::android::apex::ApexInfoList apex_info_list(apex_infos);
-    com::android::apex::write(oss, apex_info_list);
-    const std::string kApexInfoList = kApexTop + "/apex-info-list.xml";
-    if (!android::base::WriteStringToFile(oss.str(), kApexInfoList)) {
-        PLOG(ERROR) << "Failed to write " << kApexInfoList;
-        return false;
-    }
-    if (selinux_android_restorecon(kApexInfoList.c_str(), 0) != 0) {
-        PLOG(ERROR) << "selinux_android_restorecon(" << kApexInfoList << ") failed";
-    }
-
+// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
+// namespaces.
+static bool NeedsTwoMountNamespaces() {
+    if (!IsApexUpdatable()) return false;
+    if (IsRecoveryMode()) return false;
+    // In microdroid, there's only one set of APEXes in built-in directories include block devices.
+    if (IsMicrodroid()) return false;
     return true;
 }
 
-#endif  // ACTIVATE_FLATTENED_APEX
-
 static android::base::unique_fd bootstrap_ns_fd;
 static android::base::unique_fd default_ns_fd;
 
@@ -260,7 +171,7 @@
     // number of essential APEXes (e.g. com.android.runtime) are activated.
     // In the namespace for post-apexd processes, all APEXes are activated.
     bool success = true;
-    if (IsApexUpdatable() && !IsRecoveryMode()) {
+    if (NeedsTwoMountNamespaces()) {
         // Creating a new namespace by cloning, saving, and switching back to
         // the original namespace.
         if (unshare(CLONE_NEWNS) == -1) {
@@ -279,9 +190,7 @@
         default_ns_fd.reset(OpenMountNamespace());
         default_ns_id = GetMountNamespaceId();
     }
-#ifdef ACTIVATE_FLATTENED_APEX
-    success &= ActivateFlattenedApexesIfPossible();
-#endif
+
     LOG(INFO) << "SetupMountNamespaces done";
     return success;
 }
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 0e788e4..c5f1ee4 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -1035,6 +1035,20 @@
                         return;
                     }
                 }
+            } else if (reboot_target == "quiescent") {
+                bootloader_message boot = {};
+                if (std::string err; !read_bootloader_message(&boot, &err)) {
+                    LOG(ERROR) << "Failed to read bootloader message: " << err;
+                }
+                // Update the boot command field if it's empty, and preserve
+                // the other arguments in the bootloader message.
+                if (!CommandIsPresent(&boot)) {
+                    strlcpy(boot.command, "boot-quiescent", sizeof(boot.command));
+                    if (std::string err; !write_bootloader_message(boot, &err)) {
+                        LOG(ERROR) << "Failed to set bootloader message: " << err;
+                        return;
+                    }
+                }
             } else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
                        reboot_target == "fastboot") {
                 std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
diff --git a/init/service.cpp b/init/service.cpp
index 5af81bf..489dd67 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -194,8 +194,6 @@
                   << ") process group...";
         int max_processes = 0;
         int r;
-
-        flags_ |= SVC_STOPPING;
         if (signal == SIGTERM) {
             r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes);
         } else {
@@ -271,6 +269,9 @@
 
     // Remove any socket resources we may have created.
     for (const auto& socket : sockets_) {
+        if (socket.persist) {
+            continue;
+        }
         auto path = ANDROID_SOCKET_DIR "/" + socket.name;
         unlink(path.c_str());
     }
@@ -279,8 +280,7 @@
         f(siginfo);
     }
 
-    if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_ &&
-        !(flags_ & SVC_STOPPING)) {
+    if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
         LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
         trigger_shutdown(*on_failure_reboot_target_);
     }
@@ -290,7 +290,7 @@
     if (flags_ & SVC_TEMPORARY) return;
 
     pid_ = 0;
-    flags_ &= ~(SVC_RUNNING | SVC_STOPPING);
+    flags_ &= (~SVC_RUNNING);
     start_order_ = 0;
 
     // Oneshot processes go into the disabled state on exit,
@@ -412,10 +412,7 @@
     }
 
     bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
-    // Starting a service removes it from the disabled or reset state and
-    // immediately takes it out of the restarting state if it was in there.
-    flags_ &= (~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START |
-                 SVC_STOPPING));
+    ResetFlagsForStart();
 
     // Running processes require no additional work --- if they're in the
     // process of exiting, we've ensured that they will immediately restart
@@ -626,6 +623,23 @@
     return {};
 }
 
+void Service::SetStartedInFirstStage(pid_t pid) {
+    LOG(INFO) << "adding first-stage service '" << name_ << "'...";
+
+    time_started_ = boot_clock::now();  // not accurate, but doesn't matter here
+    pid_ = pid;
+    flags_ |= SVC_RUNNING;
+    start_order_ = next_start_order_++;
+
+    NotifyStateChange("running");
+}
+
+void Service::ResetFlagsForStart() {
+    // Starting a service removes it from the disabled or reset state and
+    // immediately takes it out of the restarting state if it was in there.
+    flags_ &= ~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START);
+}
+
 Result<void> Service::StartIfNotDisabled() {
     if (!(flags_ & SVC_DISABLED)) {
         return Start();
@@ -796,5 +810,18 @@
                                      nullptr, str_args, false);
 }
 
+// This is used for snapuserd_proxy, which hands off a socket to snapuserd. It's
+// a special case to support the daemon launched in first-stage init. The persist
+// feature is not part of the init language and is only used here.
+bool Service::MarkSocketPersistent(const std::string& socket_name) {
+    for (auto& socket : sockets_) {
+        if (socket.name == socket_name) {
+            socket.persist = true;
+            return true;
+        }
+    }
+    return false;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/service.h b/init/service.h
index 89b1f09..ccf6899 100644
--- a/init/service.h
+++ b/init/service.h
@@ -54,7 +54,6 @@
                                      // should not be killed during shutdown
 #define SVC_TEMPORARY 0x1000  // This service was started by 'exec' and should be removed from the
                               // service list once it is reaped.
-#define SVC_STOPPING 0x2000  // service is being stopped by init
 
 #define NR_SVC_SUPP_GIDS 12    // twelve supplementary groups
 
@@ -100,6 +99,8 @@
     void AddReapCallback(std::function<void(const siginfo_t& siginfo)> callback) {
         reap_callbacks_.emplace_back(std::move(callback));
     }
+    void SetStartedInFirstStage(pid_t pid);
+    bool MarkSocketPersistent(const std::string& socket_name);
     size_t CheckAllCommands() const { return onrestart_.CheckAllCommands(); }
 
     static bool is_exec_service_running() { return is_exec_service_running_; }
@@ -145,6 +146,7 @@
     void StopOrReset(int how);
     void KillProcessGroup(int signal, bool report_oneshot = false);
     void SetProcessAttributesAndCaps();
+    void ResetFlagsForStart();
 
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
diff --git a/init/service_utils.h b/init/service_utils.h
index 1e0b4bd..9b65dca 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -54,6 +54,7 @@
     int perm = 0;
     std::string context;
     bool passcred = false;
+    bool persist = false;
 
     // Create() creates the named unix domain socket in /dev/socket and returns a Descriptor object.
     // It should be called when starting a service, before calling fork(), such that the socket is
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 40467b7..b8c2fd2 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -33,10 +33,10 @@
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include <libsnapshot/snapshot.h>
-#include <libsnapshot/snapuserd_client.h>
 #include <private/android_filesystem_config.h>
 #include <procinfo/process_map.h>
 #include <selinux/android.h>
+#include <snapuserd/snapuserd_client.h>
 
 #include "block_dev_initializer.h"
 #include "service_utils.h"
@@ -54,6 +54,7 @@
 static constexpr char kSnapuserdPath[] = "/system/bin/snapuserd";
 static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
 static constexpr char kSnapuserdFirstStageFdVar[] = "FIRST_STAGE_SNAPUSERD_FD";
+static constexpr char kSnapuserdFirstStageInfoVar[] = "FIRST_STAGE_SNAPUSERD_INFO";
 static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0";
 static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0";
 
@@ -87,6 +88,14 @@
         _exit(127);
     }
 
+    auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 10s);
+    if (!client) {
+        LOG(FATAL) << "Could not connect to first-stage snapuserd";
+    }
+    if (client->SupportsSecondStageSocketHandoff()) {
+        setenv(kSnapuserdFirstStageInfoVar, "socket", 1);
+    }
+
     setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
 
     LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
@@ -328,5 +337,13 @@
     return GetSnapuserdFirstStagePid().has_value();
 }
 
+std::vector<std::string> GetSnapuserdFirstStageInfo() {
+    const char* pid_str = getenv(kSnapuserdFirstStageInfoVar);
+    if (!pid_str) {
+        return {};
+    }
+    return android::base::Split(pid_str, ",");
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h
index a5ab652..62aee83 100644
--- a/init/snapuserd_transition.h
+++ b/init/snapuserd_transition.h
@@ -76,6 +76,9 @@
 // Return the pid of the first-stage instances of snapuserd, if it was started.
 std::optional<pid_t> GetSnapuserdFirstStagePid();
 
+// Return snapuserd info strings that were set during first-stage init.
+std::vector<std::string> GetSnapuserdFirstStageInfo();
+
 // Save an open fd to /system/bin (in the ramdisk) into an environment. This is
 // used to later execveat() snapuserd.
 void SaveRamdiskPathToSnapuserd();
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index c8a183b..a9f6958 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -22,6 +22,7 @@
     name: "libcrypto_utils",
     vendor_available: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     vndk: {
         enabled: true,
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 68b21c6..0d9f2c7 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -354,18 +354,3 @@
     defaults: ["libcutils_test_static_defaults"],
     test_config: "KernelLibcutilsTest.xml",
 }
-
-rust_bindgen {
-    name: "libcutils_bindgen",
-    wrapper_src: "rust/cutils.h",
-    crate_name: "cutils_bindgen",
-    source_stem: "bindings",
-    local_include_dirs: ["include"],
-    bindgen_flags: [
-        "--allowlist-function", "multiuser_get_app_id",
-        "--allowlist-function", "multiuser_get_uid",
-        "--allowlist-function", "multiuser_get_user_id",
-        "--allowlist-var", "AID_KEYSTORE",
-        "--allowlist-var", "AID_USER_OFFSET",
-    ],
-}
diff --git a/libcutils/rust/cutils.h b/libcutils/rust/cutils.h
deleted file mode 100644
index 9b78af6..0000000
--- a/libcutils/rust/cutils.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#pragma once
-
-#include <cutils/multiuser.h>
-#include <private/android_filesystem_config.h>
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index fbb8049..3af07b4 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -14,6 +14,7 @@
     cflags: ["-Werror"],
     defaults: ["linux_bionic_supported"],
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     export_include_dirs: ["include/"],
     local_include_dirs: ["include/"],
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 5ca0967..0734f25 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -43,6 +43,7 @@
 using android::base::GetBoolProperty;
 using android::base::StringPrintf;
 using android::base::unique_fd;
+using android::base::WriteStringToFile;
 
 static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
 static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
@@ -202,3 +203,21 @@
 
     return CgroupController(nullptr);
 }
+
+int CgroupMap::ActivateControllers(const std::string& path) const {
+    if (__builtin_available(android 30, *)) {
+        auto controller_count = ACgroupFile_getControllerCount();
+        for (uint32_t i = 0; i < controller_count; ++i) {
+            const ACgroupController* controller = ACgroupFile_getController(i);
+            if (ACgroupController_getFlags(controller) &
+                CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
+                std::string str = std::string("+") + ACgroupController_getName(controller);
+                if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
+                    return -errno;
+                }
+            }
+        }
+        return 0;
+    }
+    return -ENOSYS;
+}
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index 427d71b..22d717b 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -62,6 +62,7 @@
 
     static CgroupMap& GetInstance();
     CgroupController FindController(const std::string& name) const;
+    int ActivateControllers(const std::string& path) const;
 
   private:
     bool loaded_ = false;
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index c824376..faf945c 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -416,13 +416,15 @@
     return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
 }
 
-static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup) {
+static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup,
+                                      bool activate_controllers) {
     auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
 
     struct stat cgroup_stat;
     mode_t cgroup_mode = 0750;
     gid_t cgroup_uid = AID_SYSTEM;
     uid_t cgroup_gid = AID_SYSTEM;
+    int ret = 0;
 
     if (stat(cgroup.c_str(), &cgroup_stat) == 1) {
         PLOG(ERROR) << "Failed to get stats for " << cgroup;
@@ -436,6 +438,13 @@
         PLOG(ERROR) << "Failed to make and chown " << uid_path;
         return -errno;
     }
+    if (activate_controllers) {
+        ret = CgroupMap::GetInstance().ActivateControllers(uid_path);
+        if (ret) {
+            LOG(ERROR) << "Failed to activate controllers in " << uid_path;
+            return ret;
+        }
+    }
 
     auto uid_pid_path = ConvertUidPidToPath(cgroup.c_str(), uid, initialPid);
 
@@ -446,7 +455,6 @@
 
     auto uid_pid_procs_file = uid_pid_path + PROCESSGROUP_CGROUP_PROCS_FILE;
 
-    int ret = 0;
     if (!WriteStringToFile(std::to_string(initialPid), uid_pid_procs_file)) {
         ret = -errno;
         PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << uid_pid_procs_file;
@@ -466,14 +474,14 @@
     if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
         CgroupGetControllerPath("memory", &cgroup);
         cgroup += "/apps";
-        int ret = createProcessGroupInternal(uid, initialPid, cgroup);
+        int ret = createProcessGroupInternal(uid, initialPid, cgroup, false);
         if (ret != 0) {
             return ret;
         }
     }
 
     CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
-    return createProcessGroupInternal(uid, initialPid, cgroup);
+    return createProcessGroupInternal(uid, initialPid, cgroup, true);
 }
 
 static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a7322fa..ab0aff5 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -591,9 +591,10 @@
     # Load trusted keys from dm-verity protected partitions
     exec -- /system/bin/fsverity_init --load-verified-keys
 
+on late-fs && property:ro.product.cpu.abilist64=*
     # Set up a tracing instance for system_server to monitor error_report_end events.
     # These are sent by kernel tools like KASAN and KFENCE when a memory corruption
-    # is detected.
+    # is detected. This is only needed for 64-bit systems.
     mkdir /sys/kernel/tracing/instances/bootreceiver 0700 system system
     restorecon_recursive /sys/kernel/tracing/instances/bootreceiver
     write /sys/kernel/tracing/instances/bootreceiver/buffer_size_kb 1
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index ff6460d..155363c 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -106,9 +106,9 @@
         "keymint/service.cpp",
     ],
     shared_libs: [
-        "android.hardware.security.keymint-V1-ndk_platform",
-        "android.hardware.security.secureclock-V1-ndk_platform",
-        "android.hardware.security.sharedsecret-V1-ndk_platform",
+        "android.hardware.security.keymint-V1-ndk",
+        "android.hardware.security.secureclock-V1-ndk",
+        "android.hardware.security.sharedsecret-V1-ndk",
         "lib_android_keymaster_keymint_utils",
         "libbase",
         "libbinder_ndk",
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index 5b83e21..2fde30f 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -477,7 +477,6 @@
     if (ssdir_fd < 0) {
         ALOGE("failed to open ss root dir \"%s\": %s\n",
                dirname, strerror(errno));
-        return -1;
     }
     ssdir_name = dirname;
     return 0;
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 12521b0..6cd381f 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -28,5 +28,6 @@
 	trusty_apploader
 
 PRODUCT_PROPERTY_OVERRIDES += \
+	ro.hardware.keystore_desede=true \
 	ro.hardware.keystore=trusty \
 	ro.hardware.gatekeeper=trusty