Merge changes from topic "sysbg_aosp"

* changes:
  task_profiles: add ServicePerformance to legacy device policy
  libutils: use system_background for low prio tasks in framework
diff --git a/TEST_MAPPING b/TEST_MAPPING
deleted file mode 100644
index da7fca1..0000000
--- a/TEST_MAPPING
+++ /dev/null
@@ -1,66 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "adbd_test"
-    },
-    {
-      "name": "adb_crypto_test"
-    },
-    {
-      "name": "adb_pairing_auth_test"
-    },
-    {
-      "name": "adb_pairing_connection_test"
-    },
-    {
-      "name": "adb_tls_connection_test"
-    },
-    {
-      "name": "CtsFsMgrTestCases"
-    },
-    {
-      "name": "CtsInitTestCases"
-    },
-    {
-      "name": "debuggerd_test"
-    },
-    {
-      "name": "fs_mgr_vendor_overlay_test"
-    },
-    {
-      "name": "init_kill_services_test"
-    },
-    {
-      "name": "libpackagelistparser_test"
-    },
-    {
-      "name": "libcutils_test"
-    },
-    {
-      "name": "libmodprobe_tests"
-    },
-    {
-      "name": "libprocinfo_test"
-    },
-    {
-      "name": "libutils_test"
-    },
-    {
-      "name": "memunreachable_test"
-    },
-    {
-      "name": "memunreachable_unit_test"
-    },
-    {
-      "name": "memunreachable_binder_test"
-    },
-    {
-      "name": "propertyinfoserializer_tests"
-    }
-  ],
-  "imports": [
-    {
-      "path": "frameworks/base/tests/StagedInstallTest"
-    }
-  ]
-}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index d6ebb0d..2c878f0 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -439,6 +439,30 @@
     {"reboot,forcedsilent", 191},
     {"reboot,forcednonsilent", 192},
     {"reboot,thermal,tj", 193},
+    {"reboot,emergency", 194},
+    {"reboot,factory", 195},
+    {"reboot,fastboot", 196},
+    {"reboot,gsa,hard", 197},
+    {"reboot,gsa,soft", 198},
+    {"reboot,master_dc,fault_n", 199},
+    {"reboot,master_dc,reset", 200},
+    {"reboot,ocp", 201},
+    {"reboot,pin", 202},
+    {"reboot,rom_recovery", 203},
+    {"reboot,uvlo", 204},
+    {"reboot,uvlo,pmic,if", 205},
+    {"reboot,uvlo,pmic,main", 206},
+    {"reboot,uvlo,pmic,sub", 207},
+    {"reboot,warm", 208},
+    {"watchdog,aoc", 209},
+    {"watchdog,apc", 210},
+    {"watchdog,apc,bl,debug,early", 211},
+    {"watchdog,apc,bl,early", 212},
+    {"watchdog,apc,early", 213},
+    {"watchdog,apm", 214},
+    {"watchdog,gsa,hard", 215},
+    {"watchdog,gsa,soft", 216},
+    {"watchdog,pmucal", 217},
 };
 
 // Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index d7e8f32..0ff047f 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"],
@@ -179,7 +180,6 @@
         "libdebuggerd/backtrace.cpp",
         "libdebuggerd/gwp_asan.cpp",
         "libdebuggerd/open_files_list.cpp",
-        "libdebuggerd/scudo.cpp",
         "libdebuggerd/tombstone.cpp",
         "libdebuggerd/tombstone_proto.cpp",
         "libdebuggerd/tombstone_proto_to_text.cpp",
@@ -192,9 +192,6 @@
     include_dirs: [
         // Needed for private/bionic_fdsan.h
         "bionic/libc",
-
-        // Needed for scudo/interface.h
-        "external/scudo/standalone/include",
     ],
     header_libs: [
         "bionic_libc_platform_headers",
@@ -209,11 +206,13 @@
         "libcutils",
         "liblog",
     ],
+    runtime_libs: [
+        "libdexfile",           // libdexfile_support dependency
+    ],
 
     whole_static_libs: [
         "libasync_safe",
         "gwp_asan_crash_handler",
-        "libscudo",
         "libtombstone_proto",
         "libprocinfo",
         "libprotobuf-cpp-lite",
@@ -224,11 +223,17 @@
             exclude_static_libs: [
                 "libdexfile_support",
             ],
+            exclude_runtime_libs: [
+                "libdexfile",
+            ],
         },
         vendor_ramdisk: {
             exclude_static_libs: [
                 "libdexfile_support",
             ],
+            exclude_runtime_libs: [
+                "libdexfile",
+            ],
         },
     },
 
@@ -236,6 +241,13 @@
         debuggable: {
             cflags: ["-DROOT_POSSIBLE"],
         },
+
+        malloc_not_svelte: {
+            cflags: ["-DUSE_SCUDO"],
+            whole_static_libs: ["libscudo"],
+            srcs: ["libdebuggerd/scudo.cpp"],
+            header_libs: ["scudo_headers"],
+        },
     },
 }
 
@@ -306,10 +318,6 @@
         "gwp_asan_headers",
     ],
 
-    include_dirs: [
-        "external/scudo/standalone/include",
-    ],
-
     local_include_dirs: [
         "libdebuggerd",
     ],
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/TEST_MAPPING b/debuggerd/TEST_MAPPING
new file mode 100644
index 0000000..d5327db
--- /dev/null
+++ b/debuggerd/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "debuggerd_test"
+    }
+  ]
+}
diff --git a/debuggerd/crash_test.cpp b/debuggerd/crash_test.cpp
index c15145f..ce0f91f 100644
--- a/debuggerd/crash_test.cpp
+++ b/debuggerd/crash_test.cpp
@@ -16,6 +16,13 @@
 
 #include <stdint.h>
 
-extern "C" void crash() {
+#include "crash_test.h"
+
+extern "C" {
+
+JITDescriptor __dex_debug_descriptor = {.version = 1};
+
+void crash() {
   *reinterpret_cast<volatile char*>(0xdead) = '1';
 }
+}
diff --git a/debuggerd/crash_test.h b/debuggerd/crash_test.h
new file mode 100644
index 0000000..2a8bea3
--- /dev/null
+++ b/debuggerd/crash_test.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+// Only support V1 of these structures.
+// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
+// for information on the JIT Compilation Interface.
+// Also, see libunwindstack/GlobalDebugImpl.h for the full definition of
+// these structures.
+struct JITCodeEntry {
+  uintptr_t next;
+  uintptr_t prev;
+  uintptr_t symfile_addr;
+  uint64_t symfile_size;
+};
+
+struct JITDescriptor {
+  uint32_t version;
+  uint32_t action_flag;
+  uintptr_t relevant_entry;
+  uintptr_t first_entry;
+};
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..4394274 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -57,6 +57,7 @@
 #include <libminijail.h>
 #include <scoped_minijail.h>
 
+#include "crash_test.h"
 #include "debuggerd/handler.h"
 #include "libdebuggerd/utility.h"
 #include "protocol.h"
@@ -961,6 +962,44 @@
   ASSERT_MATCH(result, R"(Abort message: 'x{4045}')");
 }
 
+TEST_F(CrasherTest, abort_message_newline_trimmed) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    android_set_abort_message("Message with a newline.\n");
+    abort();
+  });
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(Abort message: 'Message with a newline.')");
+}
+
+TEST_F(CrasherTest, abort_message_multiple_newlines_trimmed) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    android_set_abort_message("Message with multiple newlines.\n\n\n\n\n");
+    abort();
+  });
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(Abort message: 'Message with multiple newlines.')");
+}
+
 TEST_F(CrasherTest, abort_message_backtrace) {
   int intercept_result;
   unique_fd output_fd;
@@ -1825,3 +1864,325 @@
   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");
+}
+
+static std::string format_pointer(uintptr_t ptr) {
+#if defined(__LP64__)
+  return android::base::StringPrintf("%08x'%08x", static_cast<uint32_t>(ptr >> 32),
+                                     static_cast<uint32_t>(ptr & 0xffffffff));
+#else
+  return android::base::StringPrintf("%08x", static_cast<uint32_t>(ptr & 0xffffffff));
+#endif
+}
+
+static std::string format_pointer(void* ptr) {
+  return format_pointer(reinterpret_cast<uintptr_t>(ptr));
+}
+
+static std::string format_full_pointer(uintptr_t ptr) {
+#if defined(__LP64__)
+  return android::base::StringPrintf("%016" PRIx64, ptr);
+#else
+  return android::base::StringPrintf("%08x", ptr);
+#endif
+}
+
+static std::string format_full_pointer(void* ptr) {
+  return format_full_pointer(reinterpret_cast<uintptr_t>(ptr));
+}
+
+__attribute__((__noinline__)) int crash_call(uintptr_t ptr) {
+  int* crash_ptr = reinterpret_cast<int*>(ptr);
+  *crash_ptr = 1;
+  return *crash_ptr;
+}
+
+// Verify that a fault address before the first map is properly handled.
+TEST_F(CrasherTest, fault_address_before_first_map) {
+  StartProcess([]() {
+    ASSERT_EQ(0, crash_call(0x1024));
+    _exit(0);
+  });
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x1024)");
+
+  ASSERT_MATCH(result, R"(\nmemory map \(.*\):\n)");
+
+  std::string match_str = android::base::StringPrintf(
+      R"(memory map .*:\n--->Fault address falls at %s before any mapped regions\n    )",
+      format_pointer(0x1024).c_str());
+  ASSERT_MATCH(result, match_str);
+}
+
+// Verify that a fault address after the last map is properly handled.
+TEST_F(CrasherTest, fault_address_after_last_map) {
+  uintptr_t crash_uptr = untag_address(UINTPTR_MAX - 15);
+  StartProcess([crash_uptr]() {
+    ASSERT_EQ(0, crash_call(crash_uptr));
+    _exit(0);
+  });
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+
+  std::string match_str = R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr )";
+  match_str += android::base::StringPrintf("0x%" PRIxPTR, crash_uptr);
+  ASSERT_MATCH(result, match_str);
+
+  ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+
+  // Assumes that the open files section comes after the map section.
+  // If that assumption changes, the regex below needs to change.
+  match_str = android::base::StringPrintf(
+      R"(\n--->Fault address falls at %s after any mapped regions\n\nopen files:)",
+      format_pointer(crash_uptr).c_str());
+  ASSERT_MATCH(result, match_str);
+}
+
+// Verify that a fault address between maps is properly handled.
+TEST_F(CrasherTest, fault_address_between_maps) {
+  // Create a map before the fork so it will be present in the child.
+  void* start_ptr =
+      mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  ASSERT_NE(MAP_FAILED, start_ptr);
+  // Unmap the page in the middle.
+  void* middle_ptr =
+      reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start_ptr) + getpagesize());
+  ASSERT_EQ(0, munmap(middle_ptr, getpagesize()));
+
+  StartProcess([middle_ptr]() {
+    ASSERT_EQ(0, crash_call(reinterpret_cast<uintptr_t>(middle_ptr)));
+    _exit(0);
+  });
+
+  // Unmap the two maps.
+  ASSERT_EQ(0, munmap(start_ptr, getpagesize()));
+  void* end_ptr =
+      reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start_ptr) + 2 * getpagesize());
+  ASSERT_EQ(0, munmap(end_ptr, getpagesize()));
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+
+  std::string match_str = R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr )";
+  match_str += android::base::StringPrintf("%p", middle_ptr);
+  ASSERT_MATCH(result, match_str);
+
+  ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+
+  match_str = android::base::StringPrintf(
+      R"(    %s.*\n--->Fault address falls at %s between mapped regions\n    %s)",
+      format_pointer(start_ptr).c_str(), format_pointer(middle_ptr).c_str(),
+      format_pointer(end_ptr).c_str());
+  ASSERT_MATCH(result, match_str);
+}
+
+// Verify that a fault address happens in the correct map.
+TEST_F(CrasherTest, fault_address_in_map) {
+  // Create a map before the fork so it will be present in the child.
+  void* ptr = mmap(nullptr, getpagesize(), 0, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  ASSERT_NE(MAP_FAILED, ptr);
+
+  StartProcess([ptr]() {
+    ASSERT_EQ(0, crash_call(reinterpret_cast<uintptr_t>(ptr)));
+    _exit(0);
+  });
+
+  ASSERT_EQ(0, munmap(ptr, getpagesize()));
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+
+  std::string match_str = R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\), fault addr )";
+  match_str += android::base::StringPrintf("%p", ptr);
+  ASSERT_MATCH(result, match_str);
+
+  ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+
+  match_str = android::base::StringPrintf(R"(\n--->%s.*\n)", format_pointer(ptr).c_str());
+  ASSERT_MATCH(result, match_str);
+}
+
+static constexpr uint32_t kDexData[] = {
+    0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
+    0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
+    0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
+    0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
+    0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
+    0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
+    0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
+    0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
+    0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
+    0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
+    0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
+    0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
+    0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+    0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
+    0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
+    0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
+    0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
+};
+
+TEST_F(CrasherTest, verify_dex_pc_with_function_name) {
+  StartProcess([]() {
+    TemporaryDir td;
+    std::string tmp_so_name;
+    if (!CopySharedLibrary(td.path, &tmp_so_name)) {
+      _exit(1);
+    }
+
+    // In order to cause libunwindstack to look for this __dex_debug_descriptor
+    // move the library to which has a basename of libart.so.
+    std::string art_so_name = android::base::Dirname(tmp_so_name) + "/libart.so";
+    ASSERT_EQ(0, rename(tmp_so_name.c_str(), art_so_name.c_str()));
+    void* handle = dlopen(art_so_name.c_str(), RTLD_NOW | RTLD_LOCAL);
+    if (handle == nullptr) {
+      _exit(1);
+    }
+
+    void* ptr =
+        mmap(nullptr, sizeof(kDexData), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+    ASSERT_TRUE(ptr != MAP_FAILED);
+    memcpy(ptr, kDexData, sizeof(kDexData));
+    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, sizeof(kDexData), "dex");
+
+    JITCodeEntry dex_entry = {.symfile_addr = reinterpret_cast<uintptr_t>(ptr),
+                              .symfile_size = sizeof(kDexData)};
+
+    JITDescriptor* dex_debug =
+        reinterpret_cast<JITDescriptor*>(dlsym(handle, "__dex_debug_descriptor"));
+    ASSERT_TRUE(dex_debug != nullptr);
+    dex_debug->version = 1;
+    dex_debug->action_flag = 0;
+    dex_debug->relevant_entry = 0;
+    dex_debug->first_entry = reinterpret_cast<uintptr_t>(&dex_entry);
+
+    // This sets the magic dex pc value for register 0, using the value
+    // of register 1 + 0x102.
+    asm(".cfi_escape "
+        "0x16 /* DW_CFA_val_expression */, 0, 0x0a /* size */,"
+        "0x0c /* DW_OP_const4u */, 0x44, 0x45, 0x58, 0x31, /* magic = 'DEX1' */"
+        "0x13 /* DW_OP_drop */,"
+        "0x92 /* DW_OP_bregx */, 1, 0x82, 0x02 /* 2-byte SLEB128 */");
+
+    // For each different architecture, set register one to the dex ptr mmap
+    // created above. Then do a nullptr dereference to force a crash.
+#if defined(__arm__)
+    asm volatile(
+        "mov r1, %[base]\n"
+        "mov r2, 0\n"
+        "str r3, [r2]\n"
+        : [base] "+r"(ptr)
+        :
+        : "r1", "r2", "r3", "memory");
+#elif defined(__aarch64__)
+    asm volatile(
+        "mov x1, %[base]\n"
+        "mov x2, 0\n"
+        "str x3, [x2]\n"
+        : [base] "+r"(ptr)
+        :
+        : "x1", "x2", "x3", "memory");
+#elif defined(__i386__)
+    asm volatile(
+        "mov %[base], %%ecx\n"
+        "movl $0, %%edi\n"
+        "movl 0(%%edi), %%edx\n"
+        : [base] "+r"(ptr)
+        :
+        : "edi", "ecx", "edx", "memory");
+#elif defined(__x86_64__)
+    asm volatile(
+        "mov %[base], %%rdx\n"
+        "movq 0, %%rdi\n"
+        "movq 0(%%rdi), %%rcx\n"
+        : [base] "+r"(ptr)
+        :
+        : "rcx", "rdx", "rdi", "memory");
+#else
+#error "Unsupported architecture"
+#endif
+    _exit(0);
+  });
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+
+  // Verify the process crashed properly.
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x0)");
+
+  // Now verify that the dex_pc frame includes a proper function name.
+  ASSERT_MATCH(result, R"( \[anon:dex\] \(Main\.\<init\>\+2)");
+}
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/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 9c01f15..534d7be 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -36,6 +36,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -57,10 +58,13 @@
 #include "libdebuggerd/backtrace.h"
 #include "libdebuggerd/gwp_asan.h"
 #include "libdebuggerd/open_files_list.h"
-#include "libdebuggerd/scudo.h"
 #include "libdebuggerd/utility.h"
 #include "util.h"
 
+#if defined(USE_SCUDO)
+#include "libdebuggerd/scudo.h"
+#endif
+
 #include "gwp_asan/common.h"
 #include "gwp_asan/crash_handler.h"
 
@@ -115,8 +119,26 @@
   return "";
 }
 
-static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps,
-                                unwindstack::Regs* regs) {
+static void dump_probable_cause(log_t* log, unwindstack::Unwinder* unwinder,
+                                const ProcessInfo& process_info, const ThreadInfo& main_thread) {
+#if defined(USE_SCUDO)
+  ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
+  if (scudo_crash_data.CrashIsMine()) {
+    scudo_crash_data.DumpCause(log, unwinder);
+    return;
+  }
+#endif
+
+  GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info,
+                                       main_thread);
+  if (gwp_asan_crash_data.CrashIsMine()) {
+    gwp_asan_crash_data.DumpCause(log);
+    return;
+  }
+
+  unwindstack::Maps* maps = unwinder->GetMaps();
+  unwindstack::Regs* regs = main_thread.registers.get();
+  const siginfo_t* si = main_thread.siginfo;
   std::string cause;
   if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
     if (si->si_addr < reinterpret_cast<void*>(4096)) {
@@ -232,6 +254,12 @@
     return;
   }
 
+  // Remove any trailing newlines.
+  size_t index = length;
+  while (index > 0 && (msg[index - 1] == '\0' || msg[index - 1] == '\n')) {
+    --index;
+  }
+  msg[index] = '\0';
   _LOG(log, logtype::HEADER, "Abort message: '%s'\n", &msg[0]);
 }
 
@@ -389,22 +417,11 @@
     dump_signal_info(log, thread_info, process_info, unwinder->GetProcessMemory().get());
   }
 
-  std::unique_ptr<GwpAsanCrashData> gwp_asan_crash_data;
-  std::unique_ptr<ScudoCrashData> scudo_crash_data;
   if (primary_thread) {
-    gwp_asan_crash_data = std::make_unique<GwpAsanCrashData>(unwinder->GetProcessMemory().get(),
-                                                             process_info, thread_info);
-    scudo_crash_data =
-        std::make_unique<ScudoCrashData>(unwinder->GetProcessMemory().get(), process_info);
-  }
+    // The main thread must have a valid siginfo.
+    CHECK(thread_info.siginfo != nullptr);
+    dump_probable_cause(log, unwinder, process_info, thread_info);
 
-  if (primary_thread && gwp_asan_crash_data->CrashIsMine()) {
-    gwp_asan_crash_data->DumpCause(log);
-  } else if (thread_info.siginfo && !(primary_thread && scudo_crash_data->CrashIsMine())) {
-    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
-  }
-
-  if (primary_thread) {
     dump_abort_message(log, unwinder->GetProcessMemory().get(), process_info.abort_msg_address);
   }
 
@@ -426,16 +443,17 @@
   }
 
   if (primary_thread) {
-    if (gwp_asan_crash_data->HasDeallocationTrace()) {
-      gwp_asan_crash_data->DumpDeallocationTrace(log, unwinder);
+    GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info,
+                                         thread_info);
+
+    if (gwp_asan_crash_data.HasDeallocationTrace()) {
+      gwp_asan_crash_data.DumpDeallocationTrace(log, unwinder);
     }
 
-    if (gwp_asan_crash_data->HasAllocationTrace()) {
-      gwp_asan_crash_data->DumpAllocationTrace(log, unwinder);
+    if (gwp_asan_crash_data.HasAllocationTrace()) {
+      gwp_asan_crash_data.DumpAllocationTrace(log, unwinder);
     }
 
-    scudo_crash_data->DumpCause(log, unwinder);
-
     unwindstack::Maps* maps = unwinder->GetMaps();
     dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
                          thread_info.registers.get());
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index ff12017..6c380a1 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -18,7 +18,9 @@
 
 #include "libdebuggerd/tombstone.h"
 #include "libdebuggerd/gwp_asan.h"
+#if defined(USE_SCUDO)
 #include "libdebuggerd/scudo.h"
+#endif
 
 #include <errno.h>
 #include <fcntl.h>
@@ -28,6 +30,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/sysinfo.h>
 #include <time.h>
 
 #include <memory>
@@ -37,6 +40,7 @@
 #include <async_safe/log.h>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -185,11 +189,13 @@
 
 static void dump_probable_cause(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
                                 const ProcessInfo& process_info, const ThreadInfo& main_thread) {
+#if defined(USE_SCUDO)
   ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
   if (scudo_crash_data.CrashIsMine()) {
     scudo_crash_data.AddCauseProtos(tombstone, unwinder);
     return;
   }
+#endif
 
   GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info,
                                        main_thread);
@@ -271,6 +277,13 @@
     return;
   }
 
+  // Remove any trailing newlines.
+  size_t index = msg.size();
+  while (index > 0 && (msg[index - 1] == '\0' || msg[index - 1] == '\n')) {
+    --index;
+  }
+  msg.resize(index);
+
   tombstone->set_abort_message(msg);
 }
 
@@ -589,14 +602,6 @@
   }
 }
 
-static std::optional<uint64_t> read_uptime_secs() {
-  std::string uptime;
-  if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
-    return {};
-  }
-  return strtoll(uptime.c_str(), nullptr, 10);
-}
-
 void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
                              const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                              const ProcessInfo& process_info, const OpenFilesList* open_files) {
@@ -607,27 +612,25 @@
   result.set_revision(android::base::GetProperty("ro.revision", "unknown"));
   result.set_timestamp(get_timestamp());
 
-  std::optional<uint64_t> system_uptime = read_uptime_secs();
-  if (system_uptime) {
-    android::procinfo::ProcessInfo proc_info;
-    std::string error;
-    if (android::procinfo::GetProcessInfo(target_thread, &proc_info, &error)) {
-      uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK);
-      result.set_process_uptime(*system_uptime - starttime);
-    } else {
-      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read process info: %s",
-                            error.c_str());
-    }
-  } else {
-    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read /proc/uptime: %s",
-                          strerror(errno));
-  }
-
   const ThreadInfo& main_thread = threads.at(target_thread);
   result.set_pid(main_thread.pid);
   result.set_tid(main_thread.tid);
   result.set_uid(main_thread.uid);
   result.set_selinux_label(main_thread.selinux_label);
+  // The main thread must have a valid siginfo.
+  CHECK(main_thread.siginfo != nullptr);
+
+  struct sysinfo si;
+  sysinfo(&si);
+  android::procinfo::ProcessInfo proc_info;
+  std::string error;
+  if (android::procinfo::GetProcessInfo(main_thread.pid, &proc_info, &error)) {
+    uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK);
+    result.set_process_uptime(si.uptime - starttime);
+  } else {
+    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read process info: %s",
+                          error.c_str());
+  }
 
   auto cmd_line = result.mutable_command_line();
   for (const auto& arg : main_thread.command_line) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 053299a..681b963 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -362,8 +362,13 @@
   print_thread_memory_dump(callback, tombstone, thread);
 
   CBS("");
-  CBS("memory map (%d %s):", tombstone.memory_mappings().size(),
-      tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
+
+  // No memory maps to print.
+  if (tombstone.memory_mappings().empty()) {
+    CBS("No memory maps found");
+    return;
+  }
+
   int word_size = pointer_width(tombstone);
   const auto format_pointer = [word_size](uint64_t ptr) -> std::string {
     if (word_size == 8) {
@@ -375,8 +380,41 @@
     return StringPrintf("%0*" PRIx64, word_size * 2, ptr);
   };
 
+  std::string memory_map_header =
+      StringPrintf("memory map (%d %s):", tombstone.memory_mappings().size(),
+                   tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
+
+  bool has_fault_address = signal_info.has_fault_address();
+  uint64_t fault_address = untag_address(signal_info.fault_address());
+  bool preamble_printed = false;
+  bool printed_fault_address_marker = false;
   for (const auto& map : tombstone.memory_mappings()) {
+    if (!preamble_printed) {
+      preamble_printed = true;
+      if (has_fault_address) {
+        if (fault_address < map.begin_address()) {
+          memory_map_header +=
+              StringPrintf("\n--->Fault address falls at %s before any mapped regions",
+                           format_pointer(fault_address).c_str());
+          printed_fault_address_marker = true;
+        } else {
+          memory_map_header += " (fault address prefixed with --->)";
+        }
+      }
+      CBS("%s", memory_map_header.c_str());
+    }
+
     std::string line = "    ";
+    if (has_fault_address && !printed_fault_address_marker) {
+      if (fault_address < map.begin_address()) {
+        printed_fault_address_marker = true;
+        CBS("--->Fault address falls at %s between mapped regions",
+            format_pointer(fault_address).c_str());
+      } else if (fault_address >= map.begin_address() && fault_address < map.end_address()) {
+        printed_fault_address_marker = true;
+        line = "--->";
+      }
+    }
     StringAppendF(&line, "%s-%s", format_pointer(map.begin_address()).c_str(),
                   format_pointer(map.end_address() - 1).c_str());
     StringAppendF(&line, " %s%s%s", map.read() ? "r" : "-", map.write() ? "w" : "-",
@@ -398,6 +436,11 @@
 
     CBS("%s", line.c_str());
   }
+
+  if (has_fault_address && !printed_fault_address_marker) {
+    CBS("--->Fault address falls at %s after any mapped regions",
+        format_pointer(fault_address).c_str());
+  }
 }
 
 void print_logs(CallbackType callback, const Tombstone& tombstone, int tail) {
diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
index 4eac0e9..8fd03c4 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -20,6 +20,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
 rt_sigprocmask: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 21887ab..858a338 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -19,6 +19,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
 rt_sigprocmask: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 90843fc..152697c 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -25,6 +25,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+sysinfo: 1
 
 process_vm_readv: 1
 
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
index 4eac0e9..8fd03c4 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -20,6 +20,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
 rt_sigprocmask: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
index 1585cc6..281e231 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -19,6 +19,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
 rt_sigprocmask: 1
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 05d8050..50558f7 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -449,7 +449,7 @@
   }
 
   if (crash->output.text.fd == -1) {
-    LOG(WARNING) << "missing output fd";
+    LOG(WARNING) << "skipping tombstone file creation due to intercept";
     return;
   }
 
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/README.md b/fastboot/README.md
index c224448..d3b6c1a 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -27,16 +27,16 @@
 1. Host sends a command, which is an ascii string in a single
    packet no greater than 64 bytes.
 
-2. Client response with a single packet no greater than 64 bytes.
+2. Client response with a single packet no greater than 256 bytes.
    The first four bytes of the response are "OKAY", "FAIL", "DATA",
    or "INFO".  Additional bytes may contain an (ascii) informative
    message.
 
-   a. INFO -> the remaining 60 bytes are an informative message
+   a. INFO -> the remaining 252 bytes are an informative message
       (providing progress or diagnostic messages).  They should
       be displayed and then step #2 repeats
 
-   b. FAIL -> the requested command failed.  The remaining 60 bytes
+   b. FAIL -> the requested command failed.  The remaining 252 bytes
       of the response (if present) provide a textual failure message
       to present to the user.  Stop.
 
@@ -53,13 +53,13 @@
    until the client has sent or received the number of bytes indicated
    in the "DATA" response above.
 
-4. Client responds with a single packet no greater than 64 bytes.
+4. Client responds with a single packet no greater than 256 bytes.
    The first four bytes of the response are "OKAY", "FAIL", or "INFO".
    Similar to #2:
 
-   a. INFO -> display the remaining 60 bytes and return to #4
+   a. INFO -> display the remaining 252 bytes and return to #4
 
-   b. FAIL -> display the remaining 60 bytes (if present) as a failure
+   b. FAIL -> display the remaining 252 bytes (if present) as a failure
       reason and consider the command failed.  Stop.
 
    c. OKAY -> success.  Go to #5
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/fastboot/fs.cpp b/fastboot/fs.cpp
index 458a7a1..d268a50 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -143,6 +143,13 @@
         mke2fs_args.push_back("512");
     }
 
+    if (fsOptions & (1 << FS_OPT_CASEFOLD)) {
+        mke2fs_args.push_back("-O");
+        mke2fs_args.push_back("casefold");
+        mke2fs_args.push_back("-E");
+        mke2fs_args.push_back("encoding=utf8");
+    }
+
     mke2fs_args.push_back(fileName);
 
     std::string size_str = std::to_string(partSize / block_size);
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index 5a14b63..3096905 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -28,6 +28,10 @@
 
 #include "socket.h"
 
+#ifndef _WIN32
+#include <sys/select.h>
+#endif
+
 #include <android-base/errors.h>
 #include <android-base/stringprintf.h>
 
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index d4fc2b9..cb74ae0 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",
@@ -126,10 +127,19 @@
     export_header_lib_headers: [
         "libfiemap_headers",
     ],
-    required: [
-        "e2freefrag",
-        "e2fsdroid",
-    ],
+    target: {
+        platform: {
+            required: [
+                "e2freefrag",
+                "e2fsdroid",
+            ],
+        },
+        recovery: {
+            required: [
+                "e2fsdroid.recovery",
+            ],
+        },
+    },
 }
 
 // Two variants of libfs_mgr are provided: libfs_mgr and libfs_mgr_binder.
@@ -205,7 +215,6 @@
     static_libs: [
         "libavb_user",
         "libgsid",
-        "libutils",
         "libvold_binder",
     ],
     shared_libs: [
@@ -220,6 +229,7 @@
         "liblog",
         "liblp",
         "libselinux",
+        "libutils",
     ],
     header_libs: [
         "libcutils_headers",
diff --git a/fs_mgr/OWNERS b/fs_mgr/OWNERS
index cf353a1..c6f9054 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -1,2 +1,4 @@
+# Bug component: 30545
 bowgotsai@google.com
 dvander@google.com
+elsk@google.com
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 84709b6..432aa4f 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -1,6 +1,9 @@
 {
   "presubmit": [
     {
+      "name": "CtsFsMgrTestCases"
+    },
+    {
       "name": "libdm_test"
     },
     {
@@ -13,6 +16,9 @@
       "name": "fiemap_writer_test"
     },
     {
+      "name": "fs_mgr_vendor_overlay_test"
+    },
+    {
       "name": "vts_libsnapshot_test"
     },
     {
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..07e1e6b 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,29 @@
 
     // 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::SetAutoClearStatus(loop_fd.get())) {
+        PERROR << "Failed set LO_FLAGS_AUTOCLEAR for " << loop_device;
+    }
+    if (!LoopControl::EnableDirectIo(loop_fd.get())) {
         return false;
     }
 
-    return InstallZramDevice(device);
+    return InstallZramDevice(loop_device);
 }
 
 bool fs_mgr_swapon_all(const Fstab& fstab) {
@@ -2188,16 +2234,16 @@
     return false;
 }
 
-std::string fs_mgr_get_hashtree_algorithm(const android::fs_mgr::FstabEntry& entry) {
+std::optional<HashtreeInfo> fs_mgr_get_hashtree_info(const android::fs_mgr::FstabEntry& entry) {
     if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
-        return "";
+        return {};
     }
     DeviceMapper& dm = DeviceMapper::Instance();
     std::string device = GetVerityDeviceName(entry);
 
     std::vector<DeviceMapper::TargetInfo> table;
     if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {
-        return "";
+        return {};
     }
     for (const auto& target : table) {
         if (strcmp(target.spec.target_type, "verity") != 0) {
@@ -2213,14 +2259,15 @@
         std::vector<std::string> tokens = android::base::Split(target.data, " \t\r\n");
         if (tokens[0] != "0" && tokens[0] != "1") {
             LOG(WARNING) << "Unrecognized device mapper version in " << target.data;
-            return "";
+            return {};
         }
 
-        // Hashtree algorithm is the 8th token in the output
-        return android::base::Trim(tokens[7]);
+        // Hashtree algorithm & root digest are the 8th & 9th token in the output.
+        return HashtreeInfo{.algorithm = android::base::Trim(tokens[7]),
+                            .root_digest = android::base::Trim(tokens[8])};
     }
 
-    return "";
+    return {};
 }
 
 bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
@@ -2322,7 +2369,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/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index fdaffbe..d275320 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "android-base/file.h"
 #include "fs_mgr/roots.h"
 
 #include <sys/mount.h>
@@ -39,18 +40,26 @@
     while (true) {
         auto entry = GetEntryForMountPoint(fstab, str);
         if (entry != nullptr) return entry;
-        if (str == "/") break;
-        auto slash = str.find_last_of('/');
-        if (slash == std::string::npos) break;
-        if (slash == 0) {
-            str = "/";
-        } else {
-            str = str.substr(0, slash);
-        }
+        str = android::base::Dirname(str);
+        if (!str.compare(".") || !str.compare("/")) break;
     }
     return nullptr;
 }
 
+std::vector<FstabEntry*> GetEntriesForPath(Fstab* fstab, const std::string& path) {
+    std::vector<FstabEntry*> entries;
+    if (path.empty()) return entries;
+
+    std::string str(path);
+    while (true) {
+        entries = GetEntriesForMountPoint(fstab, str);
+        if (!entries.empty()) return entries;
+        str = android::base::Dirname(str);
+        if (!str.compare(".") || !str.compare("/")) break;
+    }
+    return entries;
+}
+
 enum class MountState {
     ERROR = -1,
     NOT_MOUNTED = 0,
@@ -71,12 +80,7 @@
     return MountState::NOT_MOUNTED;
 }
 
-bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_pt) {
-    auto rec = GetEntryForPath(fstab, path);
-    if (rec == nullptr) {
-        LERROR << "unknown volume for path [" << path << "]";
-        return false;
-    }
+bool TryPathMount(FstabEntry* rec, const std::string& mount_pt) {
     if (rec->fs_type == "ramdisk") {
         // The ramdisk is always mounted.
         return true;
@@ -136,6 +140,21 @@
     return true;
 }
 
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point) {
+    auto entries = GetEntriesForPath(fstab, path);
+    if (entries.empty()) {
+        LERROR << "unknown volume for path [" << path << "]";
+        return false;
+    }
+
+    for (auto entry : entries) {
+        if (TryPathMount(entry, mount_point)) return true;
+    }
+
+    LERROR << "Failed to mount for path [" << path << "]";
+    return false;
+}
+
 bool EnsurePathUnmounted(Fstab* fstab, const std::string& path) {
     auto rec = GetEntryForPath(fstab, path);
     if (rec == nullptr) {
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 4d3ecc9..21c9989 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -22,6 +22,7 @@
 #include <linux/dm-ioctl.h>
 
 #include <functional>
+#include <optional>
 #include <string>
 
 #include <fstab/fstab.h>
@@ -68,6 +69,13 @@
     bool userdata_mounted;
 };
 
+struct HashtreeInfo {
+    // The hash algorithm used to build the merkle tree.
+    std::string algorithm;
+    // The root digest of the merkle tree.
+    std::string root_digest;
+};
+
 // fs_mgr_mount_all() updates fstab entries that reference device-mapper.
 // Returns a |MountAllResult|. The first element is one of the FS_MNG_MNTALL_* return codes
 // defined above, and the second element tells whether this call to fs_mgr_mount_all was responsible
@@ -88,9 +96,9 @@
 bool fs_mgr_load_verity_state(int* mode);
 // Returns true if verity is enabled on this particular FstabEntry.
 bool fs_mgr_is_verity_enabled(const android::fs_mgr::FstabEntry& entry);
-// Returns the hash algorithm used to build the hashtree of this particular FstabEntry. Returns an
-// empty string if the input isn't a dm-verity entry, or if there is an error.
-std::string fs_mgr_get_hashtree_algorithm(const android::fs_mgr::FstabEntry& entry);
+// Returns the verity hashtree information of this particular FstabEntry. Returns std::nullopt
+// if the input isn't a dm-verity entry, or if there is an error.
+std::optional<HashtreeInfo> fs_mgr_get_hashtree_info(const android::fs_mgr::FstabEntry& entry);
 
 bool fs_mgr_swapon_all(const android::fs_mgr::Fstab& fstab);
 bool fs_mgr_update_logical_partition(android::fs_mgr::FstabEntry* entry);
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/Android.bp b/fs_mgr/libdm/Android.bp
index 428a7f4..2bb9035 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -86,7 +86,9 @@
     name: "vts_libdm_test",
     defaults: ["libdm_test_defaults"],
     test_suites: ["vts"],
-    test_min_api_level: 29,
+    test_options: {
+        min_shipping_api_level: 29,
+    },
 }
 
 cc_fuzz {
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..332fcf5 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,7 +51,37 @@
 
 static constexpr uint64_t kSectorSize = 512;
 
-class DeviceMapper final {
+// Returns `path` without /dev/block prefix if and only if `path` starts with
+// that prefix.
+std::optional<std::string> ExtractBlockDeviceName(const std::string& path);
+
+// This interface is for testing purposes. See DeviceMapper proper for what these methods do.
+class IDeviceMapper {
+  public:
+    virtual ~IDeviceMapper() {}
+
+    struct TargetInfo {
+        struct dm_target_spec spec;
+        std::string data;
+        TargetInfo() {}
+        TargetInfo(const struct dm_target_spec& spec, const std::string& data)
+            : spec(spec), data(data) {}
+
+        bool IsOverflowSnapshot() const;
+    };
+
+    virtual bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+                              const std::chrono::milliseconds& timeout_ms) = 0;
+    virtual DmDeviceState GetState(const std::string& name) const = 0;
+    virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) = 0;
+    virtual bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) = 0;
+    virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) = 0;
+    virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) = 0;
+    virtual bool GetDeviceString(const std::string& name, std::string* dev) = 0;
+    virtual bool DeleteDeviceIfExists(const std::string& name) = 0;
+};
+
+class DeviceMapper final : public IDeviceMapper {
   public:
     class DmBlockDevice final {
       public:
@@ -89,7 +121,7 @@
     // Removes a device mapper device with the given name.
     // Returns 'true' on success, false otherwise.
     bool DeleteDevice(const std::string& name);
-    bool DeleteDeviceIfExists(const std::string& name);
+    bool DeleteDeviceIfExists(const std::string& name) override;
     // Removes a device mapper device with the given name and waits for |timeout_ms| milliseconds
     // for the corresponding block device to be deleted.
     bool DeleteDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
@@ -108,7 +140,7 @@
     // Returns the current state of the underlying device mapper device
     // with given name.
     // One of INVALID, SUSPENDED or ACTIVE.
-    DmDeviceState GetState(const std::string& name) const;
+    DmDeviceState GetState(const std::string& name) const override;
 
     // Puts the given device to the specified status, which must be either:
     // - SUSPENDED: suspend the device, or
@@ -152,7 +184,7 @@
     // not |path| is available. It is the caller's responsibility to ensure
     // there are no races.
     bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
-                      const std::chrono::milliseconds& timeout_ms);
+                      const std::chrono::milliseconds& timeout_ms) override;
 
     // Create a device and activate the given table, without waiting to acquire
     // a valid path. If the caller will use GetDmDevicePathByName(), it should
@@ -164,7 +196,7 @@
     // process. A device with the given name must already exist.
     //
     // Returns 'true' on success, false otherwise.
-    bool LoadTableAndActivate(const std::string& name, const DmTable& table);
+    bool LoadTableAndActivate(const std::string& name, const DmTable& table) override;
 
     // Returns true if a list of available device mapper targets registered in the kernel was
     // successfully read and stored in 'targets'. Returns 'false' otherwise.
@@ -210,7 +242,7 @@
 
     // Returns a major:minor string for the named device-mapper node, that can
     // be used as inputs to DmTargets that take a block device.
-    bool GetDeviceString(const std::string& name, std::string* dev);
+    bool GetDeviceString(const std::string& name, std::string* dev) override;
 
     // The only way to create a DeviceMapper object.
     static DeviceMapper& Instance();
@@ -225,20 +257,11 @@
     // contain one TargetInfo for each target in the table. If the device does
     // not exist, or there were too many targets, the call will fail and return
     // false.
-    struct TargetInfo {
-        struct dm_target_spec spec;
-        std::string data;
-        TargetInfo() {}
-        TargetInfo(const struct dm_target_spec& spec, const std::string& data)
-            : spec(spec), data(data) {}
-
-        bool IsOverflowSnapshot() const;
-    };
-    bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
+    bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) override;
 
     // Identical to GetTableStatus, except also retrives the active table for the device
     // mapper device from the kernel.
-    bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table);
+    bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) override;
 
     static std::string GetTargetType(const struct dm_target_spec& spec);
 
@@ -255,6 +278,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/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
index ad53c11..f519054 100644
--- a/fs_mgr/libdm/include/libdm/loop_control.h
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -46,6 +46,9 @@
     // Enable Direct I/O on a loop device. This requires kernel 4.9+.
     static bool EnableDirectIo(int fd);
 
+    // Set LO_FLAGS_AUTOCLEAR on a loop device.
+    static bool SetAutoClearStatus(int fd);
+
     LoopControl(const LoopControl&) = delete;
     LoopControl& operator=(const LoopControl&) = delete;
     LoopControl& operator=(LoopControl&&) = default;
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp
index 2e40a18..32d5f38 100644
--- a/fs_mgr/libdm/loop_control.cpp
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -133,6 +133,16 @@
     return true;
 }
 
+bool LoopControl::SetAutoClearStatus(int fd) {
+    struct loop_info64 info = {};
+
+    info.lo_flags |= LO_FLAGS_AUTOCLEAR;
+    if (ioctl(fd, LOOP_SET_STATUS64, &info)) {
+        return false;
+    }
+    return true;
+}
+
 LoopDevice::LoopDevice(android::base::borrowed_fd fd, const std::chrono::milliseconds& timeout_ms,
                        bool auto_close)
     : fd_(fd), owned_fd_(-1) {
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index d16b8d6..5deba65 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -90,7 +90,9 @@
 
     test_suites: ["vts", "device-tests"],
     auto_gen_config: true,
-    test_min_api_level: 29,
+    test_options: {
+        min_shipping_api_level: 29,
+    },
     require_root: true,
 }
 
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 0cbd9db..a0ad208 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -152,6 +152,7 @@
     static_libs: [
         "libavb",
         "libdm",
+        "libext2_uuid",
         "libfs_avb",
         "libfstab",
     ],
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 0288d85..6f874a6 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -779,7 +779,7 @@
                               nullptr /* out_public_key_data */, &verify_result);
     ASSERT_EQ(0, close(hash_modified_fd.release()));
     EXPECT_NE(nullptr, vbmeta);
-    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+    // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
     EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
 
     // Modifies the auxiliary data block.
@@ -795,7 +795,7 @@
                               nullptr /* out_public_key_data */, &verify_result);
     ASSERT_EQ(0, close(aux_modified_fd.release()));
     EXPECT_NE(nullptr, vbmeta);
-    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+    // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
     EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
 
     // Resets previous modification by setting offset to -1, and checks the verification can pass.
@@ -807,7 +807,7 @@
                               nullptr /* out_public_key_data */, &verify_result);
     ASSERT_EQ(0, close(ok_fd.release()));
     EXPECT_NE(nullptr, vbmeta);
-    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+    // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
     EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
 }
 
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index fc2d8a1..4b81c2c 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -105,7 +105,9 @@
     defaults: ["liblp_test_defaults"],
     test_suites: ["vts"],
     auto_gen_config: true,
-    test_min_api_level: 29,
+    test_options: {
+        min_shipping_api_level: 29,
+    },
     require_root: true,
 }
 
diff --git a/fs_mgr/liblp/OWNERS b/fs_mgr/liblp/OWNERS
new file mode 100644
index 0000000..6a95eb2
--- /dev/null
+++ b/fs_mgr/liblp/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 391836
+dvander@google.com
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 1c44e53..5ab2ce2 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -251,7 +251,9 @@
         "vts",
         "device-tests"
     ],
-    test_min_api_level: 29,
+    test_options: {
+        min_shipping_api_level: 29,
+    },
     auto_gen_config: true,
     require_root: true,
 }
@@ -407,7 +409,9 @@
     test_suites: [
         "device-tests"
     ],
-    test_min_api_level: 30,
+    test_options: {
+        min_shipping_api_level: 30,
+    },
     auto_gen_config: true,
     require_root: false,
     host_supported: true,
@@ -504,3 +508,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..ba4044f 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;
@@ -1023,18 +1171,18 @@
     ASSERT_TRUE(writer.Initialize(cow_->fd));
 
     ASSERT_TRUE(writer.AddSequenceData(6, sequence));
-    ASSERT_TRUE(writer.AddCopy(6, 3));
+    ASSERT_TRUE(writer.AddCopy(6, 13));
     ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
     ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
     ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
-    ASSERT_TRUE(writer.AddCopy(3, 5));
-    ASSERT_TRUE(writer.AddCopy(2, 1));
+    ASSERT_TRUE(writer.AddCopy(3, 15));
+    ASSERT_TRUE(writer.AddCopy(2, 11));
     ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
     ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
-    ASSERT_TRUE(writer.AddCopy(5, 6));
+    ASSERT_TRUE(writer.AddCopy(5, 16));
     ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
-    ASSERT_TRUE(writer.AddCopy(10, 2));
-    ASSERT_TRUE(writer.AddCopy(7, 4));
+    ASSERT_TRUE(writer.AddCopy(10, 12));
+    ASSERT_TRUE(writer.AddCopy(7, 14));
     ASSERT_TRUE(writer.Finalize());
 
     // New block in cow order is 6, 12, 8, 11, 3, 2, 4, 9, 5, 1, 10, 7
@@ -1071,12 +1219,12 @@
 
     ASSERT_TRUE(writer.Initialize(cow_->fd));
 
-    ASSERT_TRUE(writer.AddCopy(2, 1));
-    ASSERT_TRUE(writer.AddCopy(10, 2));
-    ASSERT_TRUE(writer.AddCopy(6, 3));
-    ASSERT_TRUE(writer.AddCopy(7, 4));
-    ASSERT_TRUE(writer.AddCopy(3, 5));
-    ASSERT_TRUE(writer.AddCopy(5, 6));
+    ASSERT_TRUE(writer.AddCopy(2, 11));
+    ASSERT_TRUE(writer.AddCopy(10, 12));
+    ASSERT_TRUE(writer.AddCopy(6, 13));
+    ASSERT_TRUE(writer.AddCopy(7, 14));
+    ASSERT_TRUE(writer.AddCopy(3, 15));
+    ASSERT_TRUE(writer.AddCopy(5, 16));
     ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
     ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
     ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
@@ -1112,6 +1260,39 @@
     ASSERT_TRUE(iter->Done());
 }
 
+TEST_F(CowTest, InvalidMergeOrderTest) {
+    CowOptions options;
+    options.cluster_ops = 5;
+    options.num_merge_ops = 1;
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    auto writer = std::make_unique<CowWriter>(options);
+    CowReader reader;
+
+    ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+    ASSERT_TRUE(writer->AddCopy(3, 2));
+    ASSERT_TRUE(writer->AddCopy(2, 1));
+    ASSERT_TRUE(writer->AddLabel(1));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_TRUE(reader.VerifyMergeOps());
+
+    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+    ASSERT_TRUE(writer->AddCopy(4, 2));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_FALSE(reader.VerifyMergeOps());
+
+    writer = std::make_unique<CowWriter>(options);
+    ASSERT_TRUE(writer->Initialize(cow_->fd));
+    ASSERT_TRUE(writer->AddCopy(2, 1));
+    ASSERT_TRUE(writer->AddXorBlocks(3, &data, data.size(), 1, 1));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_FALSE(reader.VerifyMergeOps());
+}
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/cow_format.cpp b/fs_mgr/libsnapshot/cow_format.cpp
index 3085f80..94c4109 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)
@@ -54,14 +56,17 @@
         os << (int)op.compression << "?, ";
     os << "data_length:" << op.data_length << ",\t";
     os << "new_block:" << op.new_block << ",\t";
-    os << "source:" << op.source << ")";
+    os << "source:" << op.source;
+    if (op.type == kCowXorOp)
+        os << " (block:" << op.source / BLOCK_SZ << " offset:" << op.source % BLOCK_SZ << ")";
+    os << ")";
     return os;
 }
 
 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 +98,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..5306b28 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,24 @@
 #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->merge_op_start_ = merge_op_start_;
+    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 +155,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 +182,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 +209,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;
@@ -195,7 +231,7 @@
                 memcpy(&footer_->op, &current_op, sizeof(footer->op));
                 off_t offs = lseek(fd_.get(), pos, SEEK_SET);
                 if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
-                    PLOG(ERROR) << "lseek next op failed";
+                    PLOG(ERROR) << "lseek next op failed " << offs;
                     return false;
                 }
                 if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
@@ -215,7 +251,7 @@
         // Position for next cluster read
         off_t offs = lseek(fd_.get(), pos, SEEK_SET);
         if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
-            PLOG(ERROR) << "lseek next op failed";
+            PLOG(ERROR) << "lseek next op failed " << offs;
             return false;
         }
         ops_buffer->resize(current_op_num);
@@ -268,6 +304,7 @@
 
     ops_ = ops_buffer;
     ops_->shrink_to_fit();
+    data_loc_ = data_loc;
 
     return true;
 }
@@ -413,6 +450,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 {
@@ -425,8 +469,7 @@
 
     num_total_data_ops_ = merge_op_blocks->size();
     if (header_.num_merge_ops > 0) {
-        merge_op_blocks->erase(merge_op_blocks->begin(),
-                               merge_op_blocks->begin() + header_.num_merge_ops);
+        merge_op_start_ = header_.num_merge_ops;
     }
 
     block_map_ = block_map;
@@ -434,6 +477,44 @@
     return true;
 }
 
+bool CowReader::VerifyMergeOps() {
+    auto itr = GetMergeOpIter(true);
+    std::unordered_map<uint64_t, CowOperation> overwritten_blocks;
+    while (!itr->Done()) {
+        CowOperation op = itr->Get();
+        uint64_t block;
+        bool offset;
+        if (op.type == kCowCopyOp) {
+            block = op.source;
+            offset = false;
+        } else if (op.type == kCowXorOp) {
+            block = op.source / BLOCK_SZ;
+            offset = (op.source % BLOCK_SZ) != 0;
+        } else {
+            itr->Next();
+            continue;
+        }
+
+        CowOperation* overwrite = nullptr;
+        if (overwritten_blocks.count(block)) {
+            overwrite = &overwritten_blocks[block];
+            LOG(ERROR) << "Invalid Sequence! Block needed for op:\n"
+                       << op << "\noverwritten by previously merged op:\n"
+                       << *overwrite;
+        }
+        if (offset && overwritten_blocks.count(block + 1)) {
+            overwrite = &overwritten_blocks[block + 1];
+            LOG(ERROR) << "Invalid Sequence! Block needed for op:\n"
+                       << op << "\noverwritten by previously merged op:\n"
+                       << *overwrite;
+        }
+        if (overwrite != nullptr) return false;
+        overwritten_blocks[op.new_block] = op;
+        itr->Next();
+    }
+    return true;
+}
+
 bool CowReader::GetHeader(CowHeader* header) {
     *header = header_;
     return true;
@@ -487,7 +568,8 @@
   public:
     explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
                                std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
-                               std::shared_ptr<std::unordered_map<uint32_t, int>> map);
+                               std::shared_ptr<std::unordered_map<uint32_t, int>> map,
+                               uint64_t start);
 
     bool Done() override;
     const CowOperation& Get() override;
@@ -498,20 +580,67 @@
     std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
     std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
     std::vector<uint32_t>::reverse_iterator block_riter_;
+    uint64_t start_;
 };
 
-CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
-                                     std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
-                                     std::shared_ptr<std::unordered_map<uint32_t, int>> map) {
+class CowMergeOpIter final : public ICowOpIter {
+  public:
+    explicit CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
+                            std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
+                            std::shared_ptr<std::unordered_map<uint32_t, int>> map, uint64_t start);
+
+    bool Done() override;
+    const CowOperation& Get() override;
+    void Next() override;
+
+  private:
+    std::shared_ptr<std::vector<CowOperation>> ops_;
+    std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
+    std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
+    std::vector<uint32_t>::iterator block_iter_;
+    uint64_t start_;
+};
+
+CowMergeOpIter::CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
+                               std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
+                               std::shared_ptr<std::unordered_map<uint32_t, int>> map,
+                               uint64_t start) {
     ops_ = ops;
     merge_op_blocks_ = merge_op_blocks;
     map_ = map;
+    start_ = start;
+
+    block_iter_ = merge_op_blocks->begin() + start;
+}
+
+bool CowMergeOpIter::Done() {
+    return block_iter_ == merge_op_blocks_->end();
+}
+
+void CowMergeOpIter::Next() {
+    CHECK(!Done());
+    block_iter_++;
+}
+
+const CowOperation& CowMergeOpIter::Get() {
+    CHECK(!Done());
+    return ops_->data()[map_->at(*block_iter_)];
+}
+
+CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
+                                     std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
+                                     std::shared_ptr<std::unordered_map<uint32_t, int>> map,
+                                     uint64_t start) {
+    ops_ = ops;
+    merge_op_blocks_ = merge_op_blocks;
+    map_ = map;
+    start_ = start;
 
     block_riter_ = merge_op_blocks->rbegin();
 }
 
 bool CowRevMergeOpIter::Done() {
-    return block_riter_ == merge_op_blocks_->rend();
+    return block_riter_ == merge_op_blocks_->rend() - start_;
 }
 
 void CowRevMergeOpIter::Next() {
@@ -528,8 +657,14 @@
     return std::make_unique<CowOpIter>(ops_);
 }
 
-std::unique_ptr<ICowOpIter> CowReader::GetRevMergeOpIter() {
-    return std::make_unique<CowRevMergeOpIter>(ops_, merge_op_blocks_, block_map_);
+std::unique_ptr<ICowOpIter> CowReader::GetRevMergeOpIter(bool ignore_progress) {
+    return std::make_unique<CowRevMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+                                               ignore_progress ? 0 : merge_op_start_);
+}
+
+std::unique_ptr<ICowOpIter> CowReader::GetMergeOpIter(bool ignore_progress) {
+    return std::make_unique<CowMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+                                            ignore_progress ? 0 : merge_op_start_);
 }
 
 bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
@@ -599,7 +734,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/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index 14ce0ee..a6d96ed 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -139,5 +139,9 @@
     }
 }
 
+android::dm::IDeviceMapper& DeviceInfo::GetDeviceMapper() {
+    return android::dm::DeviceMapper::Instance();
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index 7999c99..8aefb85 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -40,6 +40,7 @@
     bool IsRecovery() const override;
     std::unique_ptr<IImageManager> OpenImageManager() const override;
     bool IsFirstStageInit() const override;
+    android::dm::IDeviceMapper& GetDeviceMapper() override;
 
     void set_first_stage_init(bool value) { first_stage_init_ = value; }
 
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..143f73c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -68,6 +68,7 @@
 
     // Return the file footer.
     virtual bool GetFooter(CowFooter* footer) = 0;
+    virtual bool VerifyMergeOps() = 0;
 
     // Return the last valid label
     virtual bool GetLastLabel(uint64_t* label) = 0;
@@ -75,8 +76,11 @@
     // Return an iterator for retrieving CowOperation entries.
     virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
 
+    // Return an iterator for retrieving CowOperation entries in reverse merge order
+    virtual std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress) = 0;
+
     // Return an iterator for retrieving CowOperation entries in merge order
-    virtual std::unique_ptr<ICowOpIter> GetRevMergeOpIter() = 0;
+    virtual std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress) = 0;
 
     // Get decoded bytes from the data section, handling any decompression.
     // All retrieved data is passed to the sink.
@@ -98,7 +102,7 @@
     virtual void Next() = 0;
 };
 
-class CowReader : public ICowReader {
+class CowReader final : public ICowReader {
   public:
     CowReader();
     ~CowReader() { owned_fd_ = {}; }
@@ -109,6 +113,7 @@
     bool Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label = {});
 
     bool InitForMerge(android::base::unique_fd&& fd);
+    bool VerifyMergeOps() override;
 
     bool GetHeader(CowHeader* header) override;
     bool GetFooter(CowFooter* footer) override;
@@ -120,7 +125,8 @@
     // whose lifetime depends on the CowOpIter object; the return
     // value of these will never be null.
     std::unique_ptr<ICowOpIter> GetOpIter() override;
-    std::unique_ptr<ICowOpIter> GetRevMergeOpIter() override;
+    std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress = false) override;
+    std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress = false) override;
 
     bool ReadData(const CowOperation& op, IByteSink* sink) override;
 
@@ -136,6 +142,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();
@@ -149,10 +158,12 @@
     std::optional<uint64_t> last_label_;
     std::shared_ptr<std::vector<CowOperation>> ops_;
     std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
+    uint64_t merge_op_start_;
     std::shared_ptr<std::unordered_map<uint32_t, int>> block_map_;
     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/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
index 0457986..b0be5a5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
@@ -36,12 +36,15 @@
 
     MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t), (override));
     MOCK_METHOD(bool, EmitRawBlocks, (uint64_t, const void*, size_t), (override));
+    MOCK_METHOD(bool, EmitXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
+                (override));
     MOCK_METHOD(bool, EmitZeroBlocks, (uint64_t, uint64_t), (override));
     MOCK_METHOD(bool, EmitLabel, (uint64_t), (override));
     MOCK_METHOD(bool, EmitSequenceData, (size_t, const uint32_t*), (override));
 
     // Open the writer in write mode (no append).
     MOCK_METHOD(bool, Initialize, (), (override));
+    MOCK_METHOD(bool, VerifyMergeOps, (), (override, const, noexcept));
 
     // Open the writer in append mode, with the last label to resume
     // from. See CowWriter::InitializeAppend.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index e60da31..55f4ed7 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -110,6 +110,7 @@
         virtual bool IsTestDevice() const { return false; }
         virtual bool IsFirstStageInit() const = 0;
         virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
+        virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0;
 
         // Helper method for implementing OpenImageManager.
         std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;
@@ -399,6 +400,7 @@
     FRIEND_TEST(SnapshotTest, MergeFailureCode);
     FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
     FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+    FRIEND_TEST(SnapshotUpdateTest, AddPartition);
     FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
@@ -610,6 +612,14 @@
     MergeFailureCode CheckMergeConsistency(LockedFile* lock, const std::string& name,
                                            const SnapshotStatus& update_status);
 
+    // Get status or table information about a device-mapper node with a single target.
+    enum class TableQuery {
+        Table,
+        Status,
+    };
+    bool GetSingleTarget(const std::string& dm_name, TableQuery query,
+                         android::dm::DeviceMapper::TargetInfo* target);
+
     // Interact with status files under /metadata/ota/snapshots.
     bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
     bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status);
@@ -772,9 +782,9 @@
     bool DeleteDeviceIfExists(const std::string& name,
                               const std::chrono::milliseconds& timeout_ms = {});
 
-    std::string gsid_dir_;
-    std::string metadata_dir_;
+    android::dm::IDeviceMapper& dm_;
     std::unique_ptr<IDeviceInfo> device_;
+    std::string metadata_dir_;
     std::unique_ptr<IImageManager> images_;
     bool use_first_stage_snapuserd_ = false;
     bool in_factory_data_reset_ = false;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index c00dafa..545f117 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -47,6 +47,7 @@
     virtual bool InitializeAppend(uint64_t label) = 0;
 
     virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
+    virtual bool VerifyMergeOps() const noexcept = 0;
 
   protected:
     android::base::borrowed_fd GetSourceFd();
@@ -58,7 +59,7 @@
 };
 
 // Send writes to a COW or a raw device directly, based on a threshold.
-class CompressedSnapshotWriter : public ISnapshotWriter {
+class CompressedSnapshotWriter final : public ISnapshotWriter {
   public:
     CompressedSnapshotWriter(const CowOptions& options);
 
@@ -70,22 +71,26 @@
     bool Finalize() override;
     uint64_t GetCowSize() override;
     std::unique_ptr<FileDescriptor> OpenReader() override;
+    bool VerifyMergeOps() const noexcept;
 
   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;
 
   private:
+    std::unique_ptr<CowReader> OpenCowReader() const;
     android::base::unique_fd cow_device_;
 
     std::unique_ptr<CowWriter> cow_;
 };
 
 // Write directly to a dm-snapshot device.
-class OnlineKernelSnapshotWriter : public ISnapshotWriter {
+class OnlineKernelSnapshotWriter final : public ISnapshotWriter {
   public:
     OnlineKernelSnapshotWriter(const CowOptions& options);
 
@@ -99,9 +104,15 @@
     uint64_t GetCowSize() override { return cow_size_; }
     std::unique_ptr<FileDescriptor> OpenReader() override;
 
+    // Online kernel snapshot writer doesn't care about merge sequences.
+    // So ignore.
+    bool VerifyMergeOps() const noexcept override { return true; }
+
   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/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 4e7ccf1..1f57bbc 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -99,6 +99,9 @@
     std::unique_ptr<IImageManager> OpenImageManager() const override {
         return IDeviceInfo::OpenImageManager("ota/test");
     }
+    android::dm::IDeviceMapper& GetDeviceMapper() override {
+        return android::dm::DeviceMapper::Instance();
+    }
 
     bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
 
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
index ed86c87..548ba00 100644
--- a/fs_mgr/libsnapshot/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/inspect_cow.cpp
@@ -16,8 +16,10 @@
 #include <stdio.h>
 #include <unistd.h>
 
+#include <iomanip>
 #include <iostream>
 #include <string>
+#include <vector>
 
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
@@ -39,17 +41,26 @@
     LOG(ERROR) << "Usage: inspect_cow [-sd] <COW_FILE>";
     LOG(ERROR) << "\t -s Run Silent";
     LOG(ERROR) << "\t -d Attempt to decompress";
-    LOG(ERROR) << "\t -b Show data for failed decompress\n";
-    LOG(ERROR) << "\t -m Show ops in reverse merge order\n";
+    LOG(ERROR) << "\t -b Show data for failed decompress";
+    LOG(ERROR) << "\t -l Show ops";
+    LOG(ERROR) << "\t -m Show ops in reverse merge order";
+    LOG(ERROR) << "\t -n Show ops in merge order";
+    LOG(ERROR) << "\t -a Include merged ops in any merge order listing";
+    LOG(ERROR) << "\t -o Shows sequence op block order";
+    LOG(ERROR) << "\t -v Verifies merge order has no conflicts\n";
 }
 
-enum OpIter { Normal, RevMerge };
+enum OpIter { Normal, RevMerge, Merge };
 
 struct Options {
     bool silent;
     bool decompress;
+    bool show_ops;
     bool show_bad;
+    bool show_seq;
+    bool verify_sequence;
     OpIter iter_type;
+    bool include_merged;
 };
 
 // Sink that always appends to the end of a string.
@@ -126,18 +137,28 @@
         }
     }
 
+    if (opt.verify_sequence) {
+        if (reader.VerifyMergeOps()) {
+            std::cout << "\nMerge sequence is consistent.\n";
+        } else {
+            std::cout << "\nMerge sequence is inconsistent!\n";
+        }
+    }
+
     std::unique_ptr<ICowOpIter> iter;
     if (opt.iter_type == Normal) {
         iter = reader.GetOpIter();
     } else if (opt.iter_type == RevMerge) {
-        iter = reader.GetRevMergeOpIter();
+        iter = reader.GetRevMergeOpIter(opt.include_merged);
+    } else if (opt.iter_type == Merge) {
+        iter = reader.GetMergeOpIter(opt.include_merged);
     }
     StringSink sink;
     bool success = true;
     while (!iter->Done()) {
         const CowOperation& op = iter->Get();
 
-        if (!opt.silent) std::cout << op << "\n";
+        if (!opt.silent && opt.show_ops) std::cout << op << "\n";
 
         if (opt.decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
             if (!reader.ReadData(op, &sink)) {
@@ -148,6 +169,24 @@
             sink.Reset();
         }
 
+        if (op.type == kCowSequenceOp && opt.show_seq) {
+            size_t read;
+            std::vector<uint32_t> merge_op_blocks;
+            size_t seq_len = op.data_length / sizeof(uint32_t);
+            merge_op_blocks.resize(seq_len);
+            if (!reader.GetRawBytes(op.source, merge_op_blocks.data(), op.data_length, &read)) {
+                PLOG(ERROR) << "Failed to read sequence op!";
+                return false;
+            }
+            if (!opt.silent) {
+                std::cout << "Sequence for " << op << " is :\n";
+                for (size_t i = 0; i < seq_len; i++) {
+                    std::cout << std::setfill('0') << std::setw(6) << merge_op_blocks[i] << ", ";
+                    if ((i + 1) % 10 == 0 || i + 1 == seq_len) std::cout << "\n";
+                }
+            }
+        }
+
         iter->Next();
     }
 
@@ -164,7 +203,9 @@
     opt.decompress = false;
     opt.show_bad = false;
     opt.iter_type = android::snapshot::Normal;
-    while ((ch = getopt(argc, argv, "sdbm")) != -1) {
+    opt.verify_sequence = false;
+    opt.include_merged = false;
+    while ((ch = getopt(argc, argv, "sdbmnolva")) != -1) {
         switch (ch) {
             case 's':
                 opt.silent = true;
@@ -178,6 +219,21 @@
             case 'm':
                 opt.iter_type = android::snapshot::RevMerge;
                 break;
+            case 'n':
+                opt.iter_type = android::snapshot::Merge;
+                break;
+            case 'o':
+                opt.show_seq = true;
+                break;
+            case 'l':
+                opt.show_ops = true;
+                break;
+            case 'v':
+                opt.verify_sequence = true;
+                break;
+            case 'a':
+                opt.include_merged = true;
+                break;
             default:
                 android::snapshot::usage();
         }
diff --git a/fs_mgr/libsnapshot/scripts/Android.bp b/fs_mgr/libsnapshot/scripts/Android.bp
new file mode 100644
index 0000000..829f5bc
--- /dev/null
+++ b/fs_mgr/libsnapshot/scripts/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+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.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index c3db32e..3d8ae29 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -114,9 +114,8 @@
     return sm;
 }
 
-SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) {
-    metadata_dir_ = device_->GetMetadataDir();
-}
+SnapshotManager::SnapshotManager(IDeviceInfo* device)
+    : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {}
 
 static std::string GetCowName(const std::string& snapshot_name) {
     return snapshot_name + "-cow";
@@ -402,8 +401,6 @@
                                    const std::chrono::milliseconds& timeout_ms, std::string* path) {
     CHECK(lock);
 
-    auto& dm = DeviceMapper::Instance();
-
     // Use an extra decoration for first-stage init, so we can transition
     // to a new table entry in second-stage.
     std::string misc_name = name;
@@ -423,7 +420,7 @@
 
     DmTable table;
     table.Emplace<DmTargetUser>(0, base_sectors, misc_name);
-    if (!dm.CreateDevice(name, table, path, timeout_ms)) {
+    if (!dm_.CreateDevice(name, table, path, timeout_ms)) {
         return false;
     }
     if (!WaitForDevice(*path, timeout_ms)) {
@@ -490,8 +487,6 @@
 
     uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;
 
-    auto& dm = DeviceMapper::Instance();
-
     // Note that merging is a global state. We do track whether individual devices
     // have completed merging, but the start of the merge process is considered
     // atomic.
@@ -518,10 +513,17 @@
             break;
     }
 
+    if (mode == SnapshotStorageMode::Persistent && status.state() == SnapshotState::MERGING) {
+        LOG(ERROR) << "Snapshot: " << name
+                   << " has snapshot status Merging but mode set to Persistent."
+                   << " Changing mode to Snapshot-Merge.";
+        mode = SnapshotStorageMode::Merge;
+    }
+
     DmTable table;
     table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
                                     kSnapshotChunkSize);
-    if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
+    if (!dm_.CreateDevice(name, table, dev_path, timeout_ms)) {
         LOG(ERROR) << "Could not create snapshot device: " << name;
         return false;
     }
@@ -652,7 +654,6 @@
 
     auto other_suffix = device_->GetOtherSlotSuffix();
 
-    auto& dm = DeviceMapper::Instance();
     for (const auto& snapshot : snapshots) {
         if (android::base::EndsWith(snapshot, other_suffix)) {
             // Allow the merge to continue, but log this unexpected case.
@@ -664,7 +665,7 @@
         // the same time. This is a fairly serious error. We could forcefully
         // map everything here, but it should have been mapped during first-
         // stage init.
-        if (dm.GetState(snapshot) == DmDeviceState::INVALID) {
+        if (dm_.GetState(snapshot) == DmDeviceState::INVALID) {
             LOG(ERROR) << "Cannot begin merge; device " << snapshot << " is not mapped.";
             return false;
         }
@@ -797,10 +798,8 @@
 }
 
 MergeFailureCode SnapshotManager::RewriteSnapshotDeviceTable(const std::string& name) {
-    auto& dm = DeviceMapper::Instance();
-
     std::vector<DeviceMapper::TargetInfo> old_targets;
-    if (!dm.GetTableInfo(name, &old_targets)) {
+    if (!dm_.GetTableInfo(name, &old_targets)) {
         LOG(ERROR) << "Could not read snapshot device table: " << name;
         return MergeFailureCode::GetTableInfo;
     }
@@ -818,7 +817,7 @@
     DmTable table;
     table.Emplace<DmTargetSnapshot>(0, old_targets[0].spec.length, base_device, cow_device,
                                     SnapshotStorageMode::Merge, kSnapshotChunkSize);
-    if (!dm.LoadTableAndActivate(name, table)) {
+    if (!dm_.LoadTableAndActivate(name, table)) {
         LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << name;
         return MergeFailureCode::ActivateNewTable;
     }
@@ -826,24 +825,18 @@
     return MergeFailureCode::Ok;
 }
 
-enum class TableQuery {
-    Table,
-    Status,
-};
-
-static bool GetSingleTarget(const std::string& dm_name, TableQuery query,
-                            DeviceMapper::TargetInfo* target) {
-    auto& dm = DeviceMapper::Instance();
-    if (dm.GetState(dm_name) == DmDeviceState::INVALID) {
+bool SnapshotManager::GetSingleTarget(const std::string& dm_name, TableQuery query,
+                                      DeviceMapper::TargetInfo* target) {
+    if (dm_.GetState(dm_name) == DmDeviceState::INVALID) {
         return false;
     }
 
     std::vector<DeviceMapper::TargetInfo> targets;
     bool result;
     if (query == TableQuery::Status) {
-        result = dm.GetTableStatus(dm_name, &targets);
+        result = dm_.GetTableStatus(dm_name, &targets);
     } else {
-        result = dm.GetTableInfo(dm_name, &targets);
+        result = dm_.GetTableInfo(dm_name, &targets);
     }
     if (!result) {
         LOG(ERROR) << "Could not query device: " << dm_name;
@@ -886,6 +879,10 @@
     if (target_type) {
         *target_type = DeviceMapper::GetTargetType(target.spec);
     }
+    if (!status->error.empty()) {
+        LOG(ERROR) << "Snapshot: " << dm_name << " returned error code: " << status->error;
+        return false;
+    }
     return true;
 }
 
@@ -1169,11 +1166,9 @@
         return MergeFailureCode::Ok;
     }
 
-    auto& dm = DeviceMapper::Instance();
-
     std::string cow_image_name = GetMappedCowDeviceName(name, status);
     std::string cow_image_path;
-    if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
+    if (!dm_.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
         LOG(ERROR) << "Failed to get path for cow device: " << cow_image_name;
         return MergeFailureCode::GetCowPathConsistencyCheck;
     }
@@ -1349,8 +1344,6 @@
 
 bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
                                              const SnapshotStatus& status) {
-    auto& dm = DeviceMapper::Instance();
-
     // Verify we have a snapshot-merge device.
     DeviceMapper::TargetInfo target;
     if (!GetSingleTarget(name, TableQuery::Table, &target)) {
@@ -1389,7 +1382,7 @@
         return false;
     }
 
-    if (!dm.LoadTableAndActivate(name, table)) {
+    if (!dm_.LoadTableAndActivate(name, table)) {
         return false;
     }
 
@@ -1452,7 +1445,7 @@
                                             std::vector<std::string>* snapuserd_argv) {
     LOG(INFO) << "Performing transition for snapuserd.";
 
-    // Don't use EnsuerSnapuserdConnected() because this is called from init,
+    // Don't use EnsureSnapuserdConnected() because this is called from init,
     // and attempting to do so will deadlock.
     if (!snapuserd_client_ && transition != InitTransition::SELINUX_DETACH) {
         snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
@@ -1462,8 +1455,6 @@
         }
     }
 
-    auto& dm = DeviceMapper::Instance();
-
     auto lock = LockExclusive();
     if (!lock) return false;
 
@@ -1477,7 +1468,7 @@
     size_t ok_cows = 0;
     for (const auto& snapshot : snapshots) {
         std::string user_cow_name = GetDmUserCowName(snapshot);
-        if (dm.GetState(user_cow_name) == DmDeviceState::INVALID) {
+        if (dm_.GetState(user_cow_name) == DmDeviceState::INVALID) {
             continue;
         }
 
@@ -1504,13 +1495,20 @@
 
         DmTable table;
         table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
-        if (!dm.LoadTableAndActivate(user_cow_name, table)) {
+        if (!dm_.LoadTableAndActivate(user_cow_name, table)) {
             LOG(ERROR) << "Unable to swap tables for " << misc_name;
             continue;
         }
 
+        std::string source_device_name;
+        if (snapshot_status.old_partition_size() > 0) {
+            source_device_name = GetSourceDeviceName(snapshot);
+        } else {
+            source_device_name = GetBaseDeviceName(snapshot);
+        }
+
         std::string source_device;
-        if (!dm.GetDmDevicePathByName(GetSourceDeviceName(snapshot), &source_device)) {
+        if (!dm_.GetDmDevicePathByName(source_device_name, &source_device)) {
             LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot);
             continue;
         }
@@ -1518,7 +1516,7 @@
         std::string cow_image_name = GetMappedCowDeviceName(snapshot, snapshot_status);
 
         std::string cow_image_device;
-        if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_device)) {
+        if (!dm_.GetDmDevicePathByName(cow_image_name, &cow_image_device)) {
             LOG(ERROR) << "Could not get device path for " << cow_image_name;
             continue;
         }
@@ -1693,8 +1691,7 @@
             // snapshot, but it's on the wrong slot. We can't unmap an active
             // partition. If this is not really a snapshot, skip the unmap
             // step.
-            auto& dm = DeviceMapper::Instance();
-            if (dm.GetState(name) == DmDeviceState::INVALID || !IsSnapshotDevice(name)) {
+            if (dm_.GetState(name) == DmDeviceState::INVALID || !IsSnapshotDevice(name)) {
                 LOG(ERROR) << "Detected snapshot " << name << " on " << current_slot << " slot"
                            << " for source partition; removing without unmap.";
                 should_unmap = false;
@@ -2031,14 +2028,13 @@
     // Create the base device for the snapshot, or if there is no snapshot, the
     // device itself. This device consists of the real blocks in the super
     // partition that this logical partition occupies.
-    auto& dm = DeviceMapper::Instance();
     std::string base_path;
     if (!CreateLogicalPartition(params, &base_path)) {
         LOG(ERROR) << "Could not create logical partition " << params.GetPartitionName()
                    << " as device " << params.GetDeviceName();
         return false;
     }
-    created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
+    created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, params.GetDeviceName());
 
     if (paths) {
         paths->target_device = base_path;
@@ -2052,7 +2048,7 @@
     // We don't have ueventd in first-stage init, so use device major:minor
     // strings instead.
     std::string base_device;
-    if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
+    if (!dm_.GetDeviceString(params.GetDeviceName(), &base_device)) {
         LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
         return false;
     }
@@ -2087,14 +2083,18 @@
     if (live_snapshot_status->compression_enabled()) {
         // Get the source device (eg the view of the partition from before it was resized).
         std::string source_device_path;
-        if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time,
-                             &source_device_path)) {
-            LOG(ERROR) << "Could not map source device for: " << cow_name;
-            return false;
-        }
+        if (live_snapshot_status->old_partition_size() > 0) {
+            if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time,
+                                 &source_device_path)) {
+                LOG(ERROR) << "Could not map source device for: " << cow_name;
+                return false;
+            }
 
-        auto source_device = GetSourceDeviceName(params.GetPartitionName());
-        created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
+            auto source_device = GetSourceDeviceName(params.GetPartitionName());
+            created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, source_device);
+        } else {
+            source_device_path = base_path;
+        }
 
         if (!WaitForDevice(source_device_path, remaining_time)) {
             return false;
@@ -2118,7 +2118,7 @@
                        << params.GetPartitionName();
             return false;
         }
-        created_devices.EmplaceBack<AutoUnmapDevice>(&dm, name);
+        created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, name);
 
         remaining_time = GetRemainingTime(params.timeout_ms, begin);
         if (remaining_time.count() < 0) return false;
@@ -2184,8 +2184,6 @@
     std::string cow_image_name = GetCowImageDeviceName(partition_name);
     *cow_name = GetCowName(partition_name);
 
-    auto& dm = DeviceMapper::Instance();
-
     // Map COW image if necessary.
     if (snapshot_status.cow_file_size() > 0) {
         if (!EnsureImageManager()) return false;
@@ -2236,11 +2234,11 @@
 
     // We have created the DmTable now. Map it.
     std::string cow_path;
-    if (!dm.CreateDevice(*cow_name, table, &cow_path, remaining_time)) {
+    if (!dm_.CreateDevice(*cow_name, table, &cow_path, remaining_time)) {
         LOG(ERROR) << "Could not create COW device: " << *cow_name;
         return false;
     }
-    created_devices->EmplaceBack<AutoUnmapDevice>(&dm, *cow_name);
+    created_devices->EmplaceBack<AutoUnmapDevice>(&dm_, *cow_name);
     LOG(INFO) << "Mapped COW device for " << params.GetPartitionName() << " at " << cow_path;
     return true;
 }
@@ -2267,10 +2265,8 @@
 }
 
 bool SnapshotManager::UnmapDmUserDevice(const std::string& snapshot_name) {
-    auto& dm = DeviceMapper::Instance();
-
     auto dm_user_name = GetDmUserCowName(snapshot_name);
-    if (dm.GetState(dm_user_name) == DmDeviceState::INVALID) {
+    if (dm_.GetState(dm_user_name) == DmDeviceState::INVALID) {
         return true;
     }
 
@@ -3490,7 +3486,6 @@
         return false;
     }
 
-    auto& dm = DeviceMapper::Instance();
     for (const auto& snapshot : snapshots) {
         SnapshotStatus status;
         if (!ReadSnapshotStatus(lock, snapshot, &status)) {
@@ -3501,7 +3496,7 @@
         }
 
         std::vector<DeviceMapper::TargetInfo> targets;
-        if (!dm.GetTableStatus(snapshot, &targets)) {
+        if (!dm_.GetTableStatus(snapshot, &targets)) {
             LOG(ERROR) << "Could not read snapshot device table: " << snapshot;
             return false;
         }
@@ -3594,11 +3589,9 @@
 // isn't running yet.
 bool SnapshotManager::GetMappedImageDevicePath(const std::string& device_name,
                                                std::string* device_path) {
-    auto& dm = DeviceMapper::Instance();
-
     // Try getting the device string if it is a device mapper device.
-    if (dm.GetState(device_name) != DmDeviceState::INVALID) {
-        return dm.GetDmDevicePathByName(device_name, device_path);
+    if (dm_.GetState(device_name) != DmDeviceState::INVALID) {
+        return dm_.GetDmDevicePathByName(device_name, device_path);
     }
 
     // Otherwise, get path from IImageManager.
@@ -3607,10 +3600,9 @@
 
 bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device_name,
                                                        std::string* device_string_or_mapped_path) {
-    auto& dm = DeviceMapper::Instance();
     // Try getting the device string if it is a device mapper device.
-    if (dm.GetState(device_name) != DmDeviceState::INVALID) {
-        return dm.GetDeviceString(device_name, device_string_or_mapped_path);
+    if (dm_.GetState(device_name) != DmDeviceState::INVALID) {
+        return dm_.GetDeviceString(device_name, device_string_or_mapped_path);
     }
 
     // Otherwise, get path from IImageManager.
@@ -3726,10 +3718,9 @@
 
 bool SnapshotManager::DeleteDeviceIfExists(const std::string& name,
                                            const std::chrono::milliseconds& timeout_ms) {
-    auto& dm = DeviceMapper::Instance();
     auto start = std::chrono::steady_clock::now();
     while (true) {
-        if (dm.DeleteDeviceIfExists(name)) {
+        if (dm_.DeleteDeviceIfExists(name)) {
             return true;
         }
         auto now = std::chrono::steady_clock::now();
@@ -3742,7 +3733,7 @@
 
     // Try to diagnose why this failed. First get the actual device path.
     std::string full_path;
-    if (!dm.GetDmDevicePathByName(name, &full_path)) {
+    if (!dm_.GetDmDevicePathByName(name, &full_path)) {
         LOG(ERROR) << "Unable to diagnose DM_DEV_REMOVE failure.";
         return false;
     }
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
index 0096f85..acee2f4 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -139,7 +139,7 @@
     auto& dm = DeviceMapper::Instance();
     std::vector<DeviceMapper::TargetInfo> table;
     if (!dm.GetTableInfo(dev_name, &table)) {
-        PCHECK(errno == ENODEV);
+        PCHECK(errno == ENODEV || errno == ENXIO);
         return {};
     }
     return table;
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 3ed27c8..c1a5af7 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -101,7 +101,8 @@
         : env_(env),
           data_(&data),
           partition_opener_(std::move(partition_opener)),
-          metadata_dir_(metadata_dir) {}
+          metadata_dir_(metadata_dir),
+          dm_(android::dm::DeviceMapper::Instance()) {}
 
     // Following APIs are mocked.
     std::string GetMetadataDir() const override { return metadata_dir_; }
@@ -125,6 +126,7 @@
     }
     bool IsRecovery() const override { return data_->is_recovery(); }
     bool IsFirstStageInit() const override { return false; }
+    android::dm::IDeviceMapper& GetDeviceMapper() override { return dm_; }
     std::unique_ptr<IImageManager> OpenImageManager() const {
         return env_->CheckCreateFakeImageManager();
     }
@@ -137,6 +139,7 @@
     std::unique_ptr<TestPartitionOpener> partition_opener_;
     std::string metadata_dir_;
     bool switched_slot_ = false;
+    android::dm::DeviceMapper& dm_;
 
     bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; }
 };
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_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index b2203fe..43c7fe2 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -963,7 +963,7 @@
     }
 
     AssertionResult UnmapAll() {
-        for (const auto& name : {"sys", "vnd", "prd"}) {
+        for (const auto& name : {"sys", "vnd", "prd", "dlkm"}) {
             if (!dm_.DeleteDeviceIfExists(name + "_a"s)) {
                 return AssertionFailure() << "Cannot unmap " << name << "_a";
             }
@@ -2073,6 +2073,80 @@
     ASSERT_LT(res.required_size(), 40_MiB);
 }
 
+TEST_F(SnapshotUpdateTest, AddPartition) {
+    // OTA client blindly unmaps all partitions that are possibly mapped.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
+    }
+
+    group_->add_partition_names("dlkm");
+
+    auto dlkm = manifest_.add_partitions();
+    dlkm->set_partition_name("dlkm");
+    dlkm->set_estimate_cow_size(2_MiB);
+    SetSize(dlkm, 3_MiB);
+
+    // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs
+    // fit in super, but not |prd|.
+    constexpr uint64_t partition_size = 3788_KiB;
+    SetSize(sys_, partition_size);
+    SetSize(vnd_, partition_size);
+    SetSize(prd_, partition_size);
+    SetSize(dlkm, partition_size);
+
+    AddOperationForPartitions({sys_, vnd_, prd_, dlkm});
+
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+    // Write some data to target partitions.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
+        ASSERT_TRUE(WriteSnapshotAndHash(name));
+    }
+
+    // Assert that source partitions aren't affected.
+    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    auto init = NewManagerForFirstStageMount("_b");
+    ASSERT_NE(init, nullptr);
+
+    ASSERT_TRUE(init->EnsureSnapuserdConnected());
+    init->set_use_first_stage_snapuserd(true);
+
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+    // Check that the target partitions have the same content.
+    std::vector<std::string> partitions = {"sys_b", "vnd_b", "prd_b", "dlkm_b"};
+    for (const auto& name : partitions) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+
+    ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
+    for (const auto& name : partitions) {
+        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(name + "-user-cow-init"));
+    }
+
+    // Initiate the merge and wait for it to be completed.
+    ASSERT_TRUE(init->InitiateMerge());
+    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+
+    // Check that the target partitions have the same content after the merge.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name))
+                << "Content of " << name << " changes after the merge";
+    }
+}
+
 class AutoKill final {
   public:
     explicit AutoKill(pid_t pid) : pid_(pid) {}
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 34b3e87..48b7d80 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -67,7 +67,7 @@
     return cow_->GetCowSize();
 }
 
-std::unique_ptr<FileDescriptor> CompressedSnapshotWriter::OpenReader() {
+std::unique_ptr<CowReader> CompressedSnapshotWriter::OpenCowReader() const {
     unique_fd cow_fd(dup(cow_device_.get()));
     if (cow_fd < 0) {
         PLOG(ERROR) << "dup COW device";
@@ -79,6 +79,20 @@
         LOG(ERROR) << "Unable to read COW";
         return nullptr;
     }
+    return cow;
+}
+
+bool CompressedSnapshotWriter::VerifyMergeOps() const noexcept {
+    auto cow_reader = OpenCowReader();
+    if (cow_reader == nullptr) {
+        LOG(ERROR) << "Couldn't open CowReader";
+        return false;
+    }
+    return cow_reader->VerifyMergeOps();
+}
+
+std::unique_ptr<FileDescriptor> CompressedSnapshotWriter::OpenReader() {
+    auto cow = OpenCowReader();
 
     auto reader = std::make_unique<CompressedSnapshotReader>();
     if (!reader->SetCow(std::move(cow))) {
@@ -106,6 +120,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 +176,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/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 1739949..6bd5323 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -73,10 +73,12 @@
         "libbrotli",
         "libcutils_sockets",
         "libdm",
+        "libfs_mgr",
         "libgflags",
         "liblog",
         "libsnapshot_cow",
         "libz",
+        "libext4_utils",
     ],
 }
 
@@ -120,12 +122,15 @@
         "libz",
         "libfs_mgr",
         "libdm",
+        "libext4_utils",
     ],
     header_libs: [
         "libstorage_literals_headers",
         "libfiemap_headers",
     ],
-    test_min_api_level: 30,
+    test_options: {
+        min_shipping_api_level: 30,
+    },
     auto_gen_config: true,
     require_root: false,
 }
diff --git a/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
index fc7153a..bff0a50 100644
--- a/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
@@ -96,6 +96,8 @@
 class CowSnapuserdTest final {
   public:
     bool Setup();
+    bool SetupOrderedOps();
+    bool SetupOrderedOpsInverted();
     bool SetupCopyOverlap_1();
     bool SetupCopyOverlap_2();
     bool Merge();
@@ -103,7 +105,10 @@
     void ReadSnapshotDeviceAndValidate();
     void Shutdown();
     void MergeInterrupt();
+    void MergeInterruptFixed(int duration);
+    void MergeInterruptRandomly(int max_duration);
     void ReadDmUserBlockWithoutDaemon();
+    void ReadLastBlock();
 
     std::string snapshot_dev() const { return snapshot_dev_->path(); }
 
@@ -117,6 +122,8 @@
     void StartMerge();
 
     void CreateCowDevice();
+    void CreateCowDeviceOrderedOps();
+    void CreateCowDeviceOrderedOpsInverted();
     void CreateCowDeviceWithCopyOverlap_1();
     void CreateCowDeviceWithCopyOverlap_2();
     bool SetupDaemon();
@@ -197,6 +204,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();
@@ -238,10 +257,77 @@
     }
 }
 
+void CowSnapuserdTest::ReadLastBlock() {
+    unique_fd rnd_fd;
+    total_base_size_ = BLOCK_SZ * 2;
+
+    base_fd_ = CreateTempFile("base_device", total_base_size_);
+    ASSERT_GE(base_fd_, 0);
+
+    rnd_fd.reset(open("/dev/random", O_RDONLY));
+    ASSERT_TRUE(rnd_fd > 0);
+
+    std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
+
+    for (size_t j = 0; j < ((total_base_size_) / BLOCK_SZ); j++) {
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), BLOCK_SZ, 0), true);
+        ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), BLOCK_SZ), true);
+    }
+
+    ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
+
+    base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
+    ASSERT_TRUE(base_loop_->valid());
+
+    std::string path = android::base::GetExecutableDirectory();
+    cow_system_ = std::make_unique<TemporaryFile>(path);
+
+    std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(total_base_size_);
+    loff_t offset = 0;
+
+    // Fill random data
+    for (size_t j = 0; j < (total_base_size_ / BLOCK_SZ); j++) {
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, BLOCK_SZ, 0),
+                  true);
+
+        offset += BLOCK_SZ;
+    }
+
+    CowOptions options;
+    options.compression = "gz";
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+
+    ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ));
+    ASSERT_TRUE(writer.AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ));
+
+    ASSERT_TRUE(writer.Finalize());
+
+    SetDeviceControlName();
+
+    StartSnapuserdDaemon();
+    InitCowDevice();
+
+    CreateDmUserDevice();
+    InitDaemon();
+
+    CreateSnapshotDevice();
+
+    unique_fd snapshot_fd(open(snapshot_dev_->path().c_str(), O_RDONLY));
+    ASSERT_TRUE(snapshot_fd > 0);
+
+    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
+
+    offset = 7680;
+    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), 512, offset), true);
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)random_buffer_1_.get() + offset, 512), 0);
+}
+
 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 +372,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 +474,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 +629,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 +665,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 +679,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 +835,7 @@
 
 void CowSnapuserdTest::SimulateDaemonRestart() {
     Shutdown();
+    std::this_thread::sleep_for(500ms);
     SetDeviceControlName();
     StartSnapuserdDaemon();
     InitCowDevice();
@@ -605,6 +844,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 +936,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 +1077,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 +1153,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();
@@ -916,6 +1211,12 @@
     harness.Shutdown();
 }
 
+TEST(Snapuserd_Test, Snapshot_END_IO_TEST) {
+    CowSnapuserdTest harness;
+    harness.ReadLastBlock();
+    harness.Shutdown();
+}
+
 TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_1) {
     CowSnapuserdTest harness;
     ASSERT_TRUE(harness.SetupCopyOverlap_1());
@@ -945,6 +1246,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/snapuserd/include/snapuserd/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index 280e857..aeecf41 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/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/snapuserd/include/snapuserd/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
index 6bb7a39..c592257 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/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/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
index ad89b24..5f4d706 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
@@ -16,10 +16,22 @@
 
 #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 <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 {
@@ -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";
 
@@ -436,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 {
@@ -491,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_) {
@@ -607,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();
     }
 
@@ -650,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
@@ -711,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
  */
@@ -739,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/snapuserd.h b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
index 95d2f77..6388a83 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
@@ -38,6 +38,7 @@
 #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>
@@ -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
index 4bf34a2..2750096 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
@@ -5,3 +5,15 @@
     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/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index a72cdc9..1ea05a3 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -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/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index 4152a64..e05822e 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -26,6 +26,8 @@
 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/snapuserd_daemon.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
index f8afac5..b660ba2 100644
--- a/fs_mgr/libsnapshot/snapuserd/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/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
index 6fc26a6..3bb7a0a 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
@@ -172,24 +172,39 @@
 }
 
 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;
+    CHECK_NE(source_offset, nullptr);
 
-    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();
+        CHECK_NE(cow_op, nullptr);
+        *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 +216,20 @@
         /*
          * 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();
+            CHECK_NE(op, nullptr);
+            uint64_t next_offset = op->source;
+            if (op->type == kCowCopyOp) {
+                next_offset *= BLOCK_SZ;
+            }
+            if (next_offset + nr_consecutive * BLOCK_SZ != *source_offset) {
                 break;
             }
             nr_consecutive += 1;
             num_ops -= 1;
             blocks.push_back(op->new_block);
-            IterNext();
+            RAIterNext();
 
             if (!overlap_) {
                 CheckOverlap(op);
@@ -247,12 +267,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 +332,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 +344,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 +378,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 +416,10 @@
         return false;
     }
 
-    InitializeIter();
+    InitializeRAIter();
     InitializeBuffer();
 
-    while (!IterDone()) {
+    while (!RAIterDone()) {
         if (!ReadAheadIOStart()) {
             return false;
         }
@@ -433,21 +455,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/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
index 8339690..2f87557 100644
--- a/fs_mgr/libsnapshot/snapuserd/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/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
index 6699189..3b6ff15 100644
--- a/fs_mgr/libsnapshot/snapuserd/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/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
index 13d45fe..5d184ad 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
@@ -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;
         }
@@ -287,16 +350,36 @@
     it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
                           Snapuserd::compare);
 
-    if (!(it != chunk_vec.end())) {
-        SNAP_LOG(ERROR) << "ReadData: Sector " << sector << " not found in chunk_vec";
-        return -1;
+    bool read_end_of_device = false;
+    if (it == chunk_vec.end()) {
+        // |-------|-------|-------|
+        // 0       1       2       3
+        //
+        // Block 0 - op 1
+        // Block 1 - op 2
+        // Block 2 - op 3
+        //
+        // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
+        //
+        // Each block is 4k bytes. Thus, the last block will span 8 sectors
+        // ranging till block 3 (However, block 3 won't be in chunk_vec as
+        // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
+        // spanning between block 2 and block 3, we need to step back
+        // and get hold of the last element.
+        //
+        // Additionally, dm-snapshot makes sure that I/O request beyond block 3
+        // will not be routed to the daemon. Hence, it is safe to assume that
+        // if a sector is not available in the chunk_vec, the I/O falls in the
+        // end of region.
+        it = std::prev(chunk_vec.end());
+        read_end_of_device = true;
     }
 
     // We didn't find the required sector; hence find the previous sector
     // as lower_bound will gives us the value greater than
     // the requested sector
     if (it->first != sector) {
-        if (it != chunk_vec.begin()) {
+        if (it != chunk_vec.begin() && !read_end_of_device) {
             --it;
         }
 
@@ -470,10 +553,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 +594,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 +640,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 +665,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 +673,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 +902,7 @@
 
 bool WorkerThread::RunThread() {
     InitializeBufsink();
+    xorsink_.Initialize(&bufsink_, BLOCK_SZ);
 
     if (!InitializeFds()) {
         return false;
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 671de9d..e97afed 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -57,14 +57,14 @@
 // Automatically unmap a device upon deletion.
 struct AutoUnmapDevice : AutoDevice {
     // On destruct, delete |name| from device mapper.
-    AutoUnmapDevice(android::dm::DeviceMapper* dm, const std::string& name)
+    AutoUnmapDevice(android::dm::IDeviceMapper* dm, const std::string& name)
         : AutoDevice(name), dm_(dm) {}
     AutoUnmapDevice(AutoUnmapDevice&& other) = default;
     ~AutoUnmapDevice();
 
   private:
     DISALLOW_COPY_AND_ASSIGN(AutoUnmapDevice);
-    android::dm::DeviceMapper* dm_ = nullptr;
+    android::dm::IDeviceMapper* dm_ = nullptr;
 };
 
 // Automatically unmap an image upon deletion.
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/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 50eee19..9a47f6b 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -46,14 +46,6 @@
 
 HealthdDraw::HealthdDraw(animation* anim)
     : kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {
-    int ret = gr_init();
-
-    if (ret < 0) {
-        LOGE("gr_init failed\n");
-        graphics_available = false;
-        return;
-    }
-
     graphics_available = true;
     sys_font = gr_sys_font();
     if (sys_font == nullptr) {
@@ -235,3 +227,11 @@
       LOGW("Charging, level unknown\n");
   }
 }
+
+std::unique_ptr<HealthdDraw> HealthdDraw::Create(animation *anim) {
+    if (gr_init() < 0) {
+        LOGE("gr_init failed\n");
+        return nullptr;
+    }
+    return std::unique_ptr<HealthdDraw>(new HealthdDraw(anim));
+}
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 7c847bd..0b48ce8 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -26,8 +26,6 @@
 
 class HealthdDraw {
  public:
-  // Configures font using given animation.
-  HealthdDraw(animation* anim);
   virtual ~HealthdDraw();
 
   // Redraws screen.
@@ -36,6 +34,8 @@
   // Blanks screen if true, unblanks if false.
   virtual void blank_screen(bool blank);
 
+  static std::unique_ptr<HealthdDraw> Create(animation *anim);
+
  protected:
   virtual void clear_screen();
 
@@ -76,6 +76,10 @@
 
   // true if minui init'ed OK, false if minui init failed
   bool graphics_available;
+
+ private:
+  // Configures font using given animation.
+  HealthdDraw(animation* anim);
 };
 
 #endif  // HEALTHD_DRAW_H
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index e95efc0..3ea90b0 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -218,9 +218,7 @@
     char* ptr;
     size_t len;
 
-    LOGW("\n");
     LOGW("*************** LAST KMSG ***************\n");
-    LOGW("\n");
     const char* kmsg[] = {
         // clang-format off
         "/sys/fs/pstore/console-ramoops-0",
@@ -263,9 +261,7 @@
     }
 
 out:
-    LOGW("\n");
     LOGW("************* END LAST KMSG *************\n");
-    LOGW("\n");
 }
 
 static int request_suspend(bool enable) {
@@ -325,7 +321,8 @@
             }
         }
 
-        healthd_draw_.reset(new HealthdDraw(&batt_anim_));
+        healthd_draw_ = HealthdDraw::Create(&batt_anim_);
+        if (healthd_draw_ == nullptr) return;
 
         if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
             healthd_draw_->blank_screen(true);
diff --git a/init/Android.bp b/init/Android.bp
index 3e8d4e3..9b02c38 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -89,7 +89,19 @@
     "host_init_verifier.cpp",
 ]
 
-cc_defaults {
+soong_config_module_type {
+    name: "libinit_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT",
+    ],
+    properties: [
+        "cflags",
+    ],
+}
+
+libinit_cc_defaults {
     name: "init_defaults",
     sanitize: {
         misc_undefined: ["signed-integer-overflow"],
@@ -109,6 +121,7 @@
         "-DDUMP_ON_UMOUNT_FAILURE=0",
         "-DSHUTDOWN_ZERO_TIMEOUT=0",
         "-DINIT_FULL_SOURCES",
+        "-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=0",
     ],
     product_variables: {
         debuggable: {
@@ -137,6 +150,14 @@
             cppflags: ["-DUSER_MODE_LINUX"],
         },
     },
+    soong_config_variables: {
+        PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT: {
+            cflags: [
+                "-UINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT",
+                "-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=1",
+            ],
+        },
+    },
     static_libs: [
         "libavb",
         "libc++fs",
@@ -228,17 +249,20 @@
     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",
+                "extra_free_kbytes.sh",
+                "make_f2fs",
+                "mke2fs",
+                "sload_f2fs",
+            ],
+        },
         recovery: {
             cflags: ["-DRECOVERY"],
             exclude_static_libs: [
@@ -248,6 +272,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"],
@@ -289,7 +321,6 @@
         "first_stage_mount.cpp",
         "reboot_utils.cpp",
         "selabel.cpp",
-        "selinux.cpp",
         "service_utils.cpp",
         "snapuserd_transition.cpp",
         "switch_root.cpp",
@@ -304,23 +335,16 @@
         "libfec",
         "libfec_rs",
         "libsquashfs_utils",
-        "liblogwrap",
-        "libext4_utils",
         "libcrypto_utils",
-        "libsparse",
         "libavb",
-        "libkeyutils",
         "liblp",
         "libcutils",
         "libbase",
         "liblog",
         "libcrypto_static",
-        "libdl",
-        "libz",
         "libselinux",
         "libcap",
         "libgsi",
-        "libcom.android.sysprop.apex",
         "liblzma",
         "libunwindstack_no_dex",
         "libbacktrace_no_dex",
@@ -431,6 +455,7 @@
         "persistent_properties_test.cpp",
         "property_service_test.cpp",
         "property_type_test.cpp",
+        "reboot_test.cpp",
         "rlimit_parser_test.cpp",
         "service_test.cpp",
         "subcontext_test.cpp",
@@ -542,3 +567,8 @@
         },
     },
 }
+
+sh_binary {
+    name: "extra_free_kbytes.sh",
+    src: "extra_free_kbytes.sh",
+}
diff --git a/init/README.md b/init/README.md
index f447ab2..58a8d6b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -693,7 +693,7 @@
   fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for
   under /odm/etc, /vendor/etc, or / at runtime, in that order.
 
-`verity_update_state <mount-point>`
+`verity_update_state`
 > Internal implementation detail used to update dm-verity state and
   set the partition._mount-point_.verified properties used by adb remount
   because fs\_mgr can't set them directly itself.
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
new file mode 100644
index 0000000..03b9eaa
--- /dev/null
+++ b/init/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsInitTestCases"
+    },
+    {
+      "name": "init_kill_services_test"
+    },
+    {
+      "name": "MicrodroidHostTestCases"
+    }
+  ]
+}
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..994eed9 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -894,9 +894,11 @@
         std::string partition = entry.mount_point == "/" ? "system" : Basename(entry.mount_point);
         SetProperty("partition." + partition + ".verified", std::to_string(mode));
 
-        std::string hash_alg = fs_mgr_get_hashtree_algorithm(entry);
-        if (!hash_alg.empty()) {
-            SetProperty("partition." + partition + ".verified.hash_alg", hash_alg);
+        auto hashtree_info = fs_mgr_get_hashtree_info(entry);
+        if (hashtree_info) {
+            SetProperty("partition." + partition + ".verified.hash_alg", hashtree_info->algorithm);
+            SetProperty("partition." + partition + ".verified.root_digest",
+                        hashtree_info->root_digest);
         }
     }
 
@@ -1305,20 +1307,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/extra_free_kbytes.sh b/init/extra_free_kbytes.sh
new file mode 100755
index 0000000..aeaa912
--- /dev/null
+++ b/init/extra_free_kbytes.sh
@@ -0,0 +1,136 @@
+#!/bin/sh
+
+# Script implements watermark_scale calculation which results in the same low
+# watermark as if extra_free_kbytes tunable were to be used.
+#
+# Usage: extra_free_kbytes.sh <extra_free_kbytes value>
+#
+# extra_free_kbytes is distributed between zones based on
+# zone.managed_pages/vm_total_pages ratio, where vm_total_pages is the sum of
+# zone.managed_pages for all zones (zone.high used in this calculation is 0
+# when this is calculated). Therefore for each zone its share is calculated as:
+#
+# extra_free_pages = extra_free_kbytes / page_size
+# extra_share = extra_free_pages * managed_pages / vm_total_pages
+#
+# This extra_share is added to the low and high watermarks:
+#
+# low = min + max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share
+# high = min + 2 * max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share
+#
+# Because Android uses extra_free_kbytes to adjust the low watermark, we ignore
+# the difference in how watermark_scale and extra_free_kbytes affect the high
+# watermark and will match the low watermark only.
+#
+# To eliminate extra_share and compansate the difference with watermark_scale,
+# a new watermark_scale_new is calculated as:
+#
+# (1) max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share =
+#   max(min / 4, managed_pages * (watermark_scale_new / 10000))
+#
+# Two cases to consider:
+# A. managed_pages * (watermark_scale / 10000) > min / 4
+# The formula (1) becomes:
+#
+# managed_pages * (watermark_scale / 10000) + extra_share =
+#   managed_pages * (watermark_scale_new / 10000)
+#
+# after simplifying and substituting extra_share formula becomes:
+#
+# (2) watermark_scale_new = watermark_scale + extra_free_pages / vm_total_pages * 10000
+#
+# B. managed_pages * (watermark_scale / 10000) < min / 4
+# The formula (1) becomes:
+#
+# min / 4 + extra_share = max(min / 4, managed_pages * (watermark_scale_new / 10000))
+#
+# after calculating watermark_scale_new, if (managed_pages * (watermark_scale_new / 10000))
+# is still smaller than min / 4 then we can't compensate extra_share with
+# watermark_scale anyway. Therefore calculation becomes:
+#
+# watermark_scale_new = (min / 4 + extra_share) / managed_pages * 10000
+#
+# after simplifying and substituting extra_share formula becomes:
+#
+# (3) watermark_scale_new = (min / 4) * 10000 / managed_pages + extra_free_pages / vm_total_pages * 10000
+#
+# After defining watermark_delta = extra_free_pages / vm_total_pages * 10000:
+#
+# if (managed_pages * (watermark_scale / 10000) > min / 4)
+#     watermark_scale_new = watermark_scale + watermark_delta
+# else
+#     watermark_scale_new = (min / 4) * 10000 / managed_pages + watermark_delta
+#
+
+if [ "$#" -ne 1 ]
+then
+    echo "Usage: $0 <extra_free_kbytes value>"
+    exit
+fi
+
+extra_free_kbytes=$1
+
+# if extra_free_kbytes knob exists, use it and exit
+if [ -e /proc/sys/vm/extra_free_kbytes ]
+then
+    echo $extra_free_kbytes > /proc/sys/vm/extra_free_kbytes
+    exit
+fi
+
+watermark_scale=`cat /proc/sys/vm/watermark_scale_factor`
+
+# convert extra_free_kbytes to pages
+page_size=$(getconf PAGESIZE)
+page_size_kb=$((page_size/1024))
+extra_free_pg=$((extra_free_kbytes/page_size_kb))
+
+managed=($(grep managed /proc/zoneinfo | awk '{print $2}'))
+length=${#managed[@]}
+min=($(grep "min" /proc/zoneinfo | awk '{print $2}'))
+
+# calculate vm_total_pages.
+# WARNING: if the final low watermark differs from the original, the source of
+# the error is likely vm_total_pages which is impossible to get exact from the
+# userspace. Grep for "Total pages" in the kernel logs to see the actual
+# vm_total_pages and plug it in the calculation to confirm the source of the
+# error. Error caused by this inaccuracy is normally within 1% range.
+vm_total_pages=0
+i=0
+while [ $i -lt $length ]
+do
+    vm_total_pages=$((vm_total_pages + managed[i]))
+    i=$((i+1))
+done
+
+# calculate watermark_scale_new for each zone and choose the max
+max_watermark_scale=0
+i=0
+while [ $i -lt $length ]
+do
+    # skip unmanaged zones
+    if [ ${managed[i]} -eq 0 ]
+    then
+        i=$((i+1))
+        continue
+    fi
+
+    base_margin=$((min[i] / 4))
+    calc_margin=$(echo "${managed[i]} * $watermark_scale / 10000" | bc)
+    # round the value by adding 0.5 and truncating the decimal part
+    watermark_delta=$(echo "x=($extra_free_pg / ($vm_total_pages / 10000) + 0.5); scale = 0; x/1" | bc -l)
+    if [ $calc_margin -gt $base_margin ]
+    then
+        watermark_scale_new=$(echo "$watermark_scale + $watermark_delta" | bc)
+    else
+        watermark_scale_new=$(echo "$base_margin / (${managed[i]} / 10000) + $watermark_delta" | bc)
+    fi
+
+    if [ $max_watermark_scale -lt $watermark_scale_new ]
+    then
+        max_watermark_scale=$watermark_scale_new
+    fi
+
+    i=$((i+1))
+done
+
+echo $max_watermark_scale > /proc/sys/vm/watermark_scale_factor
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index e2ea0ab..67cac19 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -85,7 +85,10 @@
 
 void StartConsole(const std::string& cmdline) {
     bool console = KernelConsolePresent(cmdline);
+    // Use a simple sigchld handler -- first_stage_console doesn't need to track or log zombies
+    const struct sigaction chld_act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDWAIT };
 
+    sigaction(SIGCHLD, &chld_act, nullptr);
     pid_t pid = fork();
     if (pid != 0) {
         int status;
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 78e5b60..c7b7b0c 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -330,14 +330,21 @@
     // If "/force_debuggable" is present, the second-stage init will use a userdebug
     // sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked.
     if (access("/force_debuggable", F_OK) == 0) {
+        constexpr const char adb_debug_prop_src[] = "/adb_debug.prop";
+        constexpr const char userdebug_plat_sepolicy_cil_src[] = "/userdebug_plat_sepolicy.cil";
         std::error_code ec;  // to invoke the overloaded copy_file() that won't throw.
-        if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
-            !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
-            LOG(ERROR) << "Failed to setup debug ramdisk";
-        } else {
-            // setenv for second-stage init to read above kDebugRamdisk* files.
-            setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
+        if (access(adb_debug_prop_src, F_OK) == 0 &&
+            !fs::copy_file(adb_debug_prop_src, kDebugRamdiskProp, ec)) {
+            LOG(WARNING) << "Can't copy " << adb_debug_prop_src << " to " << kDebugRamdiskProp
+                         << ": " << ec.message();
         }
+        if (access(userdebug_plat_sepolicy_cil_src, F_OK) == 0 &&
+            !fs::copy_file(userdebug_plat_sepolicy_cil_src, kDebugRamdiskSEPolicy, ec)) {
+            LOG(WARNING) << "Can't copy " << userdebug_plat_sepolicy_cil_src << " to "
+                         << kDebugRamdiskSEPolicy << ": " << ec.message();
+        }
+        // setenv for second-stage init to read above kDebugRamdisk* files.
+        setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
     }
 
     if (ForceNormalBoot(cmdline, bootconfig)) {
diff --git a/init/host_builtin_map.py b/init/host_builtin_map.py
index 6afcb17..41c86ac 100755
--- a/init/host_builtin_map.py
+++ b/init/host_builtin_map.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 """Generates the builtins map to be used by host_init_verifier.
 
 It copies the builtin function map from builtins.cpp, then replaces do_xxx() functions with the
@@ -39,8 +39,7 @@
   match = DO_REGEX.match(line)
   if match:
     if match.group(1) in check_functions:
-      print line.replace('do_', 'check_'),
+      line = line.replace('do_', 'check_')
     else:
-      print FUNCTION_REGEX.sub('check_stub', line),
-  else:
-    print line,
+      line = FUNCTION_REGEX.sub('check_stub', line)
+  print(line, end=' ')
diff --git a/init/init.cpp b/init/init.cpp
index a7325ca..e3596cb 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -27,6 +27,7 @@
 #include <sys/mount.h>
 #include <sys/signalfd.h>
 #include <sys/types.h>
+#include <sys/utsname.h>
 #include <unistd.h>
 
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
@@ -554,6 +555,19 @@
     }
 }
 
+/// Set ro.kernel.version property to contain the major.minor pair as returned
+/// by uname(2).
+static void SetKernelVersion() {
+    struct utsname uts;
+    unsigned int major, minor;
+
+    if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
+        LOG(ERROR) << "Could not parse the kernel version from uname";
+        return;
+    }
+    SetProperty("ro.kernel.version", android::base::StringPrintf("%u.%u", major, minor));
+}
+
 static void HandleSigtermSignal(const signalfd_siginfo& siginfo) {
     if (siginfo.ssi_pid != 0) {
         // Drop any userspace SIGTERM requests.
@@ -725,6 +739,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();
@@ -824,6 +872,7 @@
     export_oem_lock_status();
     MountHandler mount_handler(&epoll);
     SetUsbController();
+    SetKernelVersion();
 
     const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
     Action::set_function_map(&function_map);
@@ -852,6 +901,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..bce1cc3 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -82,110 +82,16 @@
     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 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 +166,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 +185,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/property_service.cpp b/init/property_service.cpp
index 2d67bf5..84ed58e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1017,6 +1017,31 @@
     }
 }
 
+static void property_initialize_ro_vendor_api_level() {
+    // ro.vendor.api_level shows the api_level that the vendor images (vendor, odm, ...) are
+    // required to support.
+    constexpr auto VENDOR_API_LEVEL_PROP = "ro.vendor.api_level";
+    // Candidate api levels. The order of the properties must be kept.
+    const char* VENDOR_API_LEVEL_PROPS[] = {
+            "ro.board.api_level", "ro.board.first_api_level",    "ro.product.first_api_level",
+            "ro.vndk.version",    "ro.vendor.build.version.sdk", "ro.build.version.sdk"};
+
+    for (const auto& api_level_prop : VENDOR_API_LEVEL_PROPS) {
+        int api_level = android::base::GetIntProperty(api_level_prop, 0);
+        if (api_level != 0) {
+            std::string error;
+            uint32_t res = PropertySet(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error);
+            if (res != PROP_SUCCESS) {
+                LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << api_level
+                           << ": " << error;
+            }
+            return;
+        }
+    }
+    // If no api integers are found from the vendor api level properties, ro.vendor.api_level
+    // will not be set.
+}
+
 void PropertyLoadBootDefaults() {
     // We read the properties and their values into a map, in order to always allow properties
     // loaded in the later property files to override the properties in loaded in the earlier
@@ -1102,6 +1127,7 @@
     property_derive_build_fingerprint();
     property_derive_legacy_build_fingerprint();
     property_initialize_ro_cpu_abilist();
+    property_initialize_ro_vendor_api_level();
 
     update_sys_usb_config();
 }
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 0e788e4..6aa9912 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -550,8 +550,8 @@
 
 // Like StopServices, but also logs all the services that failed to stop after the provided timeout.
 // Returns number of violators.
-static int StopServicesAndLogViolations(const std::set<std::string>& services,
-                                        std::chrono::milliseconds timeout, bool terminate) {
+int StopServicesAndLogViolations(const std::set<std::string>& services,
+                                 std::chrono::milliseconds timeout, bool terminate) {
     StopServices(services, timeout, terminate);
     int still_running = 0;
     for (const auto& s : ServiceList::GetInstance()) {
@@ -639,6 +639,7 @@
         abort();
     }
 
+    bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
     // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
     const std::set<std::string> to_starts{"watchdogd"};
     std::set<std::string> stop_first;
@@ -652,6 +653,8 @@
                            << "': " << result.error();
             }
             s->SetShutdownCritical();
+        } else if (do_shutdown_animation) {
+            continue;
         } else if (s->IsShutdownCritical()) {
             // Start shutdown critical service if not started.
             if (auto result = s->Start(); !result.ok()) {
@@ -664,14 +667,13 @@
     }
 
     // remaining operations (specifically fsck) may take a substantial duration
-    if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
+    if (!do_shutdown_animation && (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown)) {
         TurnOffBacklight();
     }
 
     Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
     Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
     if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) {
-        bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
 
         if (do_shutdown_animation) {
             SetProperty("service.bootanim.exit", "0");
@@ -1035,6 +1037,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/reboot.h b/init/reboot.h
index 81c3edc..551a114 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -17,11 +17,17 @@
 #ifndef _INIT_REBOOT_H
 #define _INIT_REBOOT_H
 
+#include <chrono>
+#include <set>
 #include <string>
 
 namespace android {
 namespace init {
 
+// Like StopServices, but also logs all the services that failed to stop after the provided timeout.
+// Returns number of violators.
+int StopServicesAndLogViolations(const std::set<std::string>& services,
+                                 std::chrono::milliseconds timeout, bool terminate);
 // Parses and handles a setprop sys.powerctl message.
 void HandlePowerctlMessage(const std::string& command);
 
diff --git a/init/reboot_test.cpp b/init/reboot_test.cpp
new file mode 100644
index 0000000..b3d038d
--- /dev/null
+++ b/init/reboot_test.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "reboot.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+#include <selinux/selinux.h>
+
+#include "builtin_arguments.h"
+#include "builtins.h"
+#include "parser.h"
+#include "service_list.h"
+#include "service_parser.h"
+#include "subcontext.h"
+#include "util.h"
+
+using namespace std::literals;
+
+using android::base::GetProperty;
+using android::base::Join;
+using android::base::SetProperty;
+using android::base::Split;
+using android::base::StringReplace;
+using android::base::WaitForProperty;
+using android::base::WriteStringToFd;
+
+namespace android {
+namespace init {
+
+class RebootTest : public ::testing::Test {
+  public:
+    RebootTest() {
+        std::vector<std::string> names = GetServiceNames();
+        if (!names.empty()) {
+            ADD_FAILURE() << "Expected empty ServiceList but found: [" << Join(names, ',') << "]";
+        }
+    }
+
+    ~RebootTest() {
+        std::vector<std::string> names = GetServiceNames();
+        for (const auto& name : names) {
+            auto s = ServiceList::GetInstance().FindService(name);
+            auto pid = s->pid();
+            ServiceList::GetInstance().RemoveService(*s);
+            if (pid > 0) {
+                kill(pid, SIGTERM);
+                kill(pid, SIGKILL);
+            }
+        }
+    }
+
+  private:
+    std::vector<std::string> GetServiceNames() const {
+        std::vector<std::string> names;
+        for (const auto& s : ServiceList::GetInstance()) {
+            names.push_back(s->name());
+        }
+        return names;
+    }
+};
+
+std::string GetSecurityContext() {
+    char* ctx;
+    if (getcon(&ctx) == -1) {
+        ADD_FAILURE() << "Failed to call getcon : " << strerror(errno);
+    }
+    std::string result = std::string(ctx);
+    freecon(ctx);
+    return result;
+}
+
+void AddTestService(const std::string& name) {
+    static constexpr std::string_view kScriptTemplate = R"init(
+service $name /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+)init";
+
+    std::string script = StringReplace(StringReplace(kScriptTemplate, "$name", name, false),
+                                       "$selabel", GetSecurityContext(), false);
+    ServiceList& service_list = ServiceList::GetInstance();
+    Parser parser;
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(WriteStringToFd(script, tf.fd));
+    ASSERT_TRUE(parser.ParseConfig(tf.path));
+}
+
+TEST_F(RebootTest, StopServicesSIGTERM) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Skipping test, must be run as root.";
+        return;
+    }
+
+    AddTestService("A");
+    AddTestService("B");
+
+    auto service_a = ServiceList::GetInstance().FindService("A");
+    ASSERT_NE(nullptr, service_a);
+    auto service_b = ServiceList::GetInstance().FindService("B");
+    ASSERT_NE(nullptr, service_b);
+
+    ASSERT_RESULT_OK(service_a->Start());
+    ASSERT_TRUE(service_a->IsRunning());
+    ASSERT_RESULT_OK(service_b->Start());
+    ASSERT_TRUE(service_b->IsRunning());
+
+    std::unique_ptr<Service> oneshot_service;
+    {
+        auto result = Service::MakeTemporaryOneshotService(
+                {"exec", GetSecurityContext(), "--", "/system/bin/yes"});
+        ASSERT_RESULT_OK(result);
+        oneshot_service = std::move(*result);
+    }
+    std::string oneshot_service_name = oneshot_service->name();
+    oneshot_service->Start();
+    ASSERT_TRUE(oneshot_service->IsRunning());
+    ServiceList::GetInstance().AddService(std::move(oneshot_service));
+
+    EXPECT_EQ(0, StopServicesAndLogViolations({"A", "B", oneshot_service_name}, 10s,
+                                              /* terminate= */ true));
+    EXPECT_FALSE(service_a->IsRunning());
+    EXPECT_FALSE(service_b->IsRunning());
+    // Oneshot services are deleted from the ServiceList after they are destroyed.
+    auto oneshot_service_after_stop = ServiceList::GetInstance().FindService(oneshot_service_name);
+    EXPECT_EQ(nullptr, oneshot_service_after_stop);
+}
+
+TEST_F(RebootTest, StopServicesSIGKILL) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Skipping test, must be run as root.";
+        return;
+    }
+
+    AddTestService("A");
+    AddTestService("B");
+
+    auto service_a = ServiceList::GetInstance().FindService("A");
+    ASSERT_NE(nullptr, service_a);
+    auto service_b = ServiceList::GetInstance().FindService("B");
+    ASSERT_NE(nullptr, service_b);
+
+    ASSERT_RESULT_OK(service_a->Start());
+    ASSERT_TRUE(service_a->IsRunning());
+    ASSERT_RESULT_OK(service_b->Start());
+    ASSERT_TRUE(service_b->IsRunning());
+
+    std::unique_ptr<Service> oneshot_service;
+    {
+        auto result = Service::MakeTemporaryOneshotService(
+                {"exec", GetSecurityContext(), "--", "/system/bin/yes"});
+        ASSERT_RESULT_OK(result);
+        oneshot_service = std::move(*result);
+    }
+    std::string oneshot_service_name = oneshot_service->name();
+    oneshot_service->Start();
+    ASSERT_TRUE(oneshot_service->IsRunning());
+    ServiceList::GetInstance().AddService(std::move(oneshot_service));
+
+    EXPECT_EQ(0, StopServicesAndLogViolations({"A", "B", oneshot_service_name}, 10s,
+                                              /* terminate= */ false));
+    EXPECT_FALSE(service_a->IsRunning());
+    EXPECT_FALSE(service_b->IsRunning());
+    // Oneshot services are deleted from the ServiceList after they are destroyed.
+    auto oneshot_service_after_stop = ServiceList::GetInstance().FindService(oneshot_service_name);
+    EXPECT_EQ(nullptr, oneshot_service_after_stop);
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 42d3023..29c0ff3 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -295,6 +295,25 @@
     return access(plat_policy_cil_file, R_OK) != -1;
 }
 
+std::optional<const char*> GetUserdebugPlatformPolicyFile() {
+    // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.
+    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
+    if (force_debuggable_env && "true"s == force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
+        const std::vector<const char*> debug_policy_candidates = {
+#if INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT == 1
+            "/system_ext/etc/selinux/userdebug_plat_sepolicy.cil",
+#endif
+            kDebugRamdiskSEPolicy,
+        };
+        for (const char* debug_policy : debug_policy_candidates) {
+            if (access(debug_policy, F_OK) == 0) {
+                return debug_policy;
+            }
+        }
+    }
+    return std::nullopt;
+}
+
 struct PolicyFile {
     unique_fd fd;
     std::string path;
@@ -310,13 +329,10 @@
     // secilc is invoked to compile the above three policy files into a single monolithic policy
     // file. This file is then loaded into the kernel.
 
-    // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.
-    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
-    bool use_userdebug_policy =
-            ((force_debuggable_env && "true"s == force_debuggable_env) &&
-             AvbHandle::IsDeviceUnlocked() && access(kDebugRamdiskSEPolicy, F_OK) == 0);
+    const auto userdebug_plat_sepolicy = GetUserdebugPlatformPolicyFile();
+    const bool use_userdebug_policy = userdebug_plat_sepolicy.has_value();
     if (use_userdebug_policy) {
-        LOG(WARNING) << "Using userdebug system sepolicy";
+        LOG(INFO) << "Using userdebug system sepolicy " << *userdebug_plat_sepolicy;
     }
 
     // Load precompiled policy from vendor image, if a matching policy is found there. The policy
@@ -413,7 +429,7 @@
     // clang-format off
     std::vector<const char*> compile_args {
         "/system/bin/secilc",
-        use_userdebug_policy ? kDebugRamdiskSEPolicy: plat_policy_cil_file,
+        use_userdebug_policy ? *userdebug_plat_sepolicy : plat_policy_cil_file,
         "-m", "-M", "true", "-G", "-N",
         "-c", version_as_string.c_str(),
         plat_mapping_file.c_str(),
diff --git a/init/service.cpp b/init/service.cpp
index c3069f5..489dd67 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -269,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());
     }
@@ -409,9 +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));
+    ResetFlagsForStart();
 
     // Running processes require no additional work --- if they're in the
     // process of exiting, we've ensured that they will immediately restart
@@ -622,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();
@@ -792,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 043555f..ccf6899 100644
--- a/init/service.h
+++ b/init/service.h
@@ -99,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_; }
@@ -144,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 9a0b3b7..b8c2fd2 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -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/init/subcontext.cpp b/init/subcontext.cpp
index f1fbffe..6eaa80f 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -44,6 +44,7 @@
 #endif
 
 using android::base::GetExecutablePath;
+using android::base::GetProperty;
 using android::base::Join;
 using android::base::Socketpair;
 using android::base::Split;
@@ -337,6 +338,11 @@
 }
 
 void InitializeSubcontext() {
+    if (IsMicrodroid()) {
+        LOG(INFO) << "Not using subcontext for microdroid";
+        return;
+    }
+
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
         subcontext.reset(
                 new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
@@ -351,6 +357,9 @@
 }
 
 bool SubcontextChildReap(pid_t pid) {
+    if (!subcontext) {
+        return false;
+    }
     if (subcontext->pid() == pid) {
         if (!subcontext_terminated_by_shutdown) {
             subcontext->Restart();
diff --git a/init/util.cpp b/init/util.cpp
index 9f7bfdb..d1e518b 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -757,5 +757,10 @@
     is_default_mount_namespace_ready = true;
 }
 
+bool IsMicrodroid() {
+    static bool is_microdroid = android::base::GetProperty("ro.hardware", "") == "microdroid";
+    return is_microdroid;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index daba852..bf53675 100644
--- a/init/util.h
+++ b/init/util.h
@@ -103,5 +103,7 @@
 
 bool IsDefaultMountNamespaceReady();
 void SetDefaultMountNamespaceReady();
+
+bool IsMicrodroid();
 }  // namespace init
 }  // namespace android
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/TEST_MAPPING b/libcutils/TEST_MAPPING
new file mode 100644
index 0000000..e512ab7
--- /dev/null
+++ b/libcutils/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libcutils_test"
+    }
+  ]
+}
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/libmodprobe/TEST_MAPPING b/libmodprobe/TEST_MAPPING
new file mode 100644
index 0000000..526b1e4
--- /dev/null
+++ b/libmodprobe/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libmodprobe_tests"
+    }
+  ]
+}
diff --git a/libpackagelistparser/TEST_MAPPING b/libpackagelistparser/TEST_MAPPING
new file mode 100644
index 0000000..51773f9
--- /dev/null
+++ b/libpackagelistparser/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libpackagelistparser_test"
+    }
+  ]
+}
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/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
index 100d60e..e704a36 100644
--- a/libprocessgroup/cgrouprc/include/android/cgrouprc.h
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <sys/cdefs.h>
 #include <stdint.h>
 
 __BEGIN_DECLS
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/libsparse/Android.bp b/libsparse/Android.bp
index 0b4b640..3f9aeb2 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -85,11 +85,11 @@
     srcs: ["simg_dump.py"],
     version: {
         py2: {
-            embedded_launcher: true,
-            enabled: true,
+            enabled: false,
         },
         py3: {
-            enabled: false,
+            embedded_launcher: true,
+            enabled: true,
         },
     },
 }
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
index 82a03ad..b0b3b22 100755
--- a/libsparse/simg_dump.py
+++ b/libsparse/simg_dump.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 
 # Copyright (C) 2012 The Android Open Source Project
 #
@@ -14,7 +14,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from __future__ import print_function
 import csv
 import getopt
 import hashlib
@@ -47,7 +46,7 @@
     opts, args = getopt.getopt(sys.argv[1:],
                                "vsc:",
                                ["verbose", "showhash", "csvfile"])
-  except getopt.GetoptError, e:
+  except getopt.GetoptError as e:
     print(e)
     usage(me)
   for o, a in opts:
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
index 2a89e29..f07e32b 100644
--- a/libstats/pull_rust/Android.bp
+++ b/libstats/pull_rust/Android.bp
@@ -57,3 +57,13 @@
         "libstatspull_bindgen",
     ],
 }
+
+rust_test {
+    name: "libstatspull_bindgen_test",
+    srcs: [":libstatspull_bindgen"],
+    crate_name: "statspull_bindgen_test",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    clippy_lints: "none",
+    lints: "none",
+}
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 14e3e35..292425a 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -20,6 +20,16 @@
 
 namespace android {
 
+namespace {
+
+constexpr uint64_t WAKE_EVENT_FD_SEQ = 1;
+
+epoll_event createEpollEvent(uint32_t events, uint64_t seq) {
+    return {.events = events, .data = {.u64 = seq}};
+}
+
+}  // namespace
+
 // --- WeakMessageHandler ---
 
 WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) :
@@ -64,7 +74,7 @@
       mSendingMessage(false),
       mPolling(false),
       mEpollRebuildRequired(false),
-      mNextRequestSeq(0),
+      mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
       mResponseIndex(0),
       mNextMessageUptime(LLONG_MAX) {
     mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
@@ -137,22 +147,17 @@
         mEpollFd.reset();
     }
 
-    // Allocate the new epoll instance and register the wake pipe.
+    // Allocate the new epoll instance and register the WakeEventFd.
     mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
-    struct epoll_event eventItem;
-    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
-    eventItem.events = EPOLLIN;
-    eventItem.data.fd = mWakeEventFd.get();
-    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
+    epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
+    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
                         strerror(errno));
 
-    for (size_t i = 0; i < mRequests.size(); i++) {
-        const Request& request = mRequests.valueAt(i);
-        struct epoll_event eventItem;
-        request.initEventItem(&eventItem);
+    for (const auto& [seq, request] : mRequests) {
+        epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
 
         int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
         if (epollResult < 0) {
@@ -276,26 +281,28 @@
 #endif
 
     for (int i = 0; i < eventCount; i++) {
-        int fd = eventItems[i].data.fd;
+        const SequenceNumber seq = eventItems[i].data.u64;
         uint32_t epollEvents = eventItems[i].events;
-        if (fd == mWakeEventFd.get()) {
+        if (seq == WAKE_EVENT_FD_SEQ) {
             if (epollEvents & EPOLLIN) {
                 awoken();
             } else {
                 ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
             }
         } else {
-            ssize_t requestIndex = mRequests.indexOfKey(fd);
-            if (requestIndex >= 0) {
+            const auto& request_it = mRequests.find(seq);
+            if (request_it != mRequests.end()) {
+                const auto& request = request_it->second;
                 int events = 0;
                 if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
                 if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
                 if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
                 if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
-                pushResponse(events, mRequests.valueAt(requestIndex));
+                mResponses.push({.seq = seq, .events = events, .request = request});
             } else {
-                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
-                        "no longer registered.", epollEvents, fd);
+                ALOGW("Ignoring unexpected epoll events 0x%x for sequence number %" PRIu64
+                      " that is no longer registered.",
+                      epollEvents, seq);
             }
         }
     }
@@ -354,7 +361,8 @@
             // we need to be a little careful when removing the file descriptor afterwards.
             int callbackResult = response.request.callback->handleEvent(fd, events, data);
             if (callbackResult == 0) {
-                removeFd(fd, response.request.seq);
+                AutoMutex _l(mLock);
+                removeSequenceNumberLocked(response.seq);
             }
 
             // Clear the callback reference in the response structure promptly because we
@@ -416,13 +424,6 @@
     TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
 }
 
-void Looper::pushResponse(int events, const Request& request) {
-    Response response;
-    response.events = events;
-    response.request = request;
-    mResponses.push(response);
-}
-
 int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
     return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : nullptr, data);
 }
@@ -449,27 +450,27 @@
 
     { // acquire lock
         AutoMutex _l(mLock);
+        // There is a sequence number reserved for the WakeEventFd.
+        if (mNextRequestSeq == WAKE_EVENT_FD_SEQ) mNextRequestSeq++;
+        const SequenceNumber seq = mNextRequestSeq++;
 
         Request request;
         request.fd = fd;
         request.ident = ident;
         request.events = events;
-        request.seq = mNextRequestSeq++;
         request.callback = callback;
         request.data = data;
-        if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1
 
-        struct epoll_event eventItem;
-        request.initEventItem(&eventItem);
-
-        ssize_t requestIndex = mRequests.indexOfKey(fd);
-        if (requestIndex < 0) {
+        epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
+        auto seq_it = mSequenceNumberByFd.find(fd);
+        if (seq_it == mSequenceNumberByFd.end()) {
             int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
             if (epollResult < 0) {
                 ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
                 return -1;
             }
-            mRequests.add(fd, request);
+            mRequests.emplace(seq, request);
+            mSequenceNumberByFd.emplace(fd, seq);
         } else {
             int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
             if (epollResult < 0) {
@@ -486,7 +487,7 @@
                     // set from scratch because it may contain an old file handle that we are
                     // now unable to remove since its file descriptor is no longer valid.
                     // No such problem would have occurred if we were using the poll system
-                    // call instead, but that approach carries others disadvantages.
+                    // call instead, but that approach carries other disadvantages.
 #if DEBUG_CALLBACKS
                     ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
                             "being recycled, falling back on EPOLL_CTL_ADD: %s",
@@ -504,71 +505,69 @@
                     return -1;
                 }
             }
-            mRequests.replaceValueAt(requestIndex, request);
+            const SequenceNumber oldSeq = seq_it->second;
+            mRequests.erase(oldSeq);
+            mRequests.emplace(seq, request);
+            seq_it->second = seq;
         }
     } // release lock
     return 1;
 }
 
 int Looper::removeFd(int fd) {
-    return removeFd(fd, -1);
+    AutoMutex _l(mLock);
+    const auto& it = mSequenceNumberByFd.find(fd);
+    if (it == mSequenceNumberByFd.end()) {
+        return 0;
+    }
+    return removeSequenceNumberLocked(it->second);
 }
 
-int Looper::removeFd(int fd, int seq) {
+int Looper::removeSequenceNumberLocked(SequenceNumber seq) {
 #if DEBUG_CALLBACKS
-    ALOGD("%p ~ removeFd - fd=%d, seq=%d", this, fd, seq);
+    ALOGD("%p ~ removeFd - fd=%d, seq=%u", this, fd, seq);
 #endif
 
-    { // acquire lock
-        AutoMutex _l(mLock);
-        ssize_t requestIndex = mRequests.indexOfKey(fd);
-        if (requestIndex < 0) {
-            return 0;
-        }
+    const auto& request_it = mRequests.find(seq);
+    if (request_it == mRequests.end()) {
+        return 0;
+    }
+    const int fd = request_it->second.fd;
 
-        // Check the sequence number if one was given.
-        if (seq != -1 && mRequests.valueAt(requestIndex).seq != seq) {
+    // Always remove the FD from the request map even if an error occurs while
+    // updating the epoll set so that we avoid accidentally leaking callbacks.
+    mRequests.erase(request_it);
+    mSequenceNumberByFd.erase(fd);
+
+    int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
+    if (epollResult < 0) {
+        if (errno == EBADF || errno == ENOENT) {
+            // Tolerate EBADF or ENOENT because it means that the file descriptor was closed
+            // before its callback was unregistered. This error may occur naturally when a
+            // callback has the side-effect of closing the file descriptor before returning and
+            // unregistering itself.
+            //
+            // Unfortunately due to kernel limitations we need to rebuild the epoll
+            // set from scratch because it may contain an old file handle that we are
+            // now unable to remove since its file descriptor is no longer valid.
+            // No such problem would have occurred if we were using the poll system
+            // call instead, but that approach carries other disadvantages.
 #if DEBUG_CALLBACKS
-            ALOGD("%p ~ removeFd - sequence number mismatch, oldSeq=%d",
-                    this, mRequests.valueAt(requestIndex).seq);
+            ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
+                  "being closed: %s",
+                  this, strerror(errno));
 #endif
-            return 0;
+            scheduleEpollRebuildLocked();
+        } else {
+            // Some other error occurred.  This is really weird because it means
+            // our list of callbacks got out of sync with the epoll set somehow.
+            // We defensively rebuild the epoll set to avoid getting spurious
+            // notifications with nowhere to go.
+            ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
+            scheduleEpollRebuildLocked();
+            return -1;
         }
-
-        // Always remove the FD from the request map even if an error occurs while
-        // updating the epoll set so that we avoid accidentally leaking callbacks.
-        mRequests.removeItemsAt(requestIndex);
-
-        int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
-        if (epollResult < 0) {
-            if (seq != -1 && (errno == EBADF || errno == ENOENT)) {
-                // Tolerate EBADF or ENOENT when the sequence number is known because it
-                // means that the file descriptor was closed before its callback was
-                // unregistered.  This error may occur naturally when a callback has the
-                // side-effect of closing the file descriptor before returning and
-                // unregistering itself.
-                //
-                // Unfortunately due to kernel limitations we need to rebuild the epoll
-                // set from scratch because it may contain an old file handle that we are
-                // now unable to remove since its file descriptor is no longer valid.
-                // No such problem would have occurred if we were using the poll system
-                // call instead, but that approach carries others disadvantages.
-#if DEBUG_CALLBACKS
-                ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
-                        "being closed: %s", this, strerror(errno));
-#endif
-                scheduleEpollRebuildLocked();
-            } else {
-                // Some other error occurred.  This is really weird because it means
-                // our list of callbacks got out of sync with the epoll set somehow.
-                // We defensively rebuild the epoll set to avoid getting spurious
-                // notifications with nowhere to go.
-                ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
-                scheduleEpollRebuildLocked();
-                return -1;
-            }
-        }
-    } // release lock
+    }
     return 1;
 }
 
@@ -656,14 +655,11 @@
     return mPolling;
 }
 
-void Looper::Request::initEventItem(struct epoll_event* eventItem) const {
-    int epollEvents = 0;
+uint32_t Looper::Request::getEpollEvents() const {
+    uint32_t epollEvents = 0;
     if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
     if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
-
-    memset(eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
-    eventItem->events = epollEvents;
-    eventItem->data.fd = fd;
+    return epollEvents;
 }
 
 MessageHandler::~MessageHandler() { }
diff --git a/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index 34f424b..c859f9c 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -8,6 +8,9 @@
 #include <utils/Looper.h>
 #include <utils/StopWatch.h>
 #include <utils/Timers.h>
+#include <thread>
+#include <unordered_map>
+#include <utility>
 #include "Looper_test_pipe.h"
 
 #include <utils/threads.h>
@@ -710,4 +713,123 @@
             << "no more messages to handle";
 }
 
+class LooperEventCallback : public LooperCallback {
+  public:
+    using Callback = std::function<int(int fd, int events)>;
+    explicit LooperEventCallback(Callback callback) : mCallback(std::move(callback)) {}
+    int handleEvent(int fd, int events, void* /*data*/) override { return mCallback(fd, events); }
+
+  private:
+    Callback mCallback;
+};
+
+// A utility class that allows for pipes to be added and removed from the looper, and polls the
+// looper from a different thread.
+class ThreadedLooperUtil {
+  public:
+    explicit ThreadedLooperUtil(const sp<Looper>& looper) : mLooper(looper), mRunning(true) {
+        mThread = std::thread([this]() {
+            while (mRunning) {
+                static constexpr std::chrono::milliseconds POLL_TIMEOUT(500);
+                mLooper->pollOnce(POLL_TIMEOUT.count());
+            }
+        });
+    }
+
+    ~ThreadedLooperUtil() {
+        mRunning = false;
+        mThread.join();
+    }
+
+    // Create a new pipe, and return the write end of the pipe and the id used to track the pipe.
+    // The read end of the pipe is added to the looper.
+    std::pair<int /*id*/, base::unique_fd> createPipe() {
+        int pipeFd[2];
+        if (pipe(pipeFd)) {
+            ADD_FAILURE() << "pipe() failed.";
+            return {};
+        }
+        const int readFd = pipeFd[0];
+        const int writeFd = pipeFd[1];
+
+        int id;
+        {  // acquire lock
+            std::scoped_lock l(mLock);
+
+            id = mNextId++;
+            mFds.emplace(id, readFd);
+
+            auto removeCallback = [this, id, readFd](int fd, int events) {
+                EXPECT_EQ(readFd, fd) << "Received callback for incorrect fd.";
+                if ((events & Looper::EVENT_HANGUP) == 0) {
+                    return 1;  // Not a hangup, keep the callback.
+                }
+                removePipe(id);
+                return 0;  // Remove the callback.
+            };
+
+            mLooper->addFd(readFd, 0, Looper::EVENT_INPUT,
+                           new LooperEventCallback(std::move(removeCallback)), nullptr);
+        }  // release lock
+
+        return {id, base::unique_fd(writeFd)};
+    }
+
+    // Remove the pipe with the given id.
+    void removePipe(int id) {
+        std::scoped_lock l(mLock);
+        if (mFds.find(id) == mFds.end()) {
+            return;
+        }
+        mLooper->removeFd(mFds[id].get());
+        mFds.erase(id);
+    }
+
+    // Check if the pipe with the given id exists and has not been removed.
+    bool hasPipe(int id) {
+        std::scoped_lock l(mLock);
+        return mFds.find(id) != mFds.end();
+    }
+
+  private:
+    sp<Looper> mLooper;
+    std::atomic<bool> mRunning;
+    std::thread mThread;
+
+    std::mutex mLock;
+    std::unordered_map<int, base::unique_fd> mFds GUARDED_BY(mLock);
+    int mNextId GUARDED_BY(mLock) = 0;
+};
+
+TEST_F(LooperTest, MultiThreaded_NoUnexpectedFdRemoval) {
+    ThreadedLooperUtil util(mLooper);
+
+    // Iterate repeatedly to try to recreate a flaky instance.
+    for (int i = 0; i < 1000; i++) {
+        auto [firstPipeId, firstPipeFd] = util.createPipe();
+        const int firstFdNumber = firstPipeFd.get();
+
+        // Close the first pipe's fd, causing a fd hangup.
+        firstPipeFd.reset();
+
+        // Request to remove the pipe from this test thread. This causes a race for pipe removal
+        // between the hangup in the looper's thread and this remove request from the test thread.
+        util.removePipe(firstPipeId);
+
+        // Create the second pipe. Since the fds for the first pipe are closed, this pipe should
+        // have the same fd numbers as the first pipe because the lowest unused fd number is used.
+        const auto [secondPipeId, fd] = util.createPipe();
+        EXPECT_EQ(firstFdNumber, fd.get())
+                << "The first and second fds must match for the purposes of this test.";
+
+        // Wait for unexpected hangup to occur.
+        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+
+        ASSERT_TRUE(util.hasPipe(secondPipeId)) << "The second pipe was removed unexpectedly.";
+
+        util.removePipe(secondPipeId);
+    }
+    SUCCEED() << "No unexpectedly removed fds.";
+}
+
 } // namespace android
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index b57e287..0518927 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -170,7 +170,7 @@
         : mStrong(INITIAL_STRONG_VALUE)
         , mWeak(0)
         , mBase(base)
-        , mFlags(0)
+        , mFlags(OBJECT_LIFETIME_STRONG)
     {
     }
 
@@ -189,7 +189,7 @@
         : mStrong(INITIAL_STRONG_VALUE)
         , mWeak(0)
         , mBase(base)
-        , mFlags(0)
+        , mFlags(OBJECT_LIFETIME_STRONG)
         , mStrongRefs(NULL)
         , mWeakRefs(NULL)
         , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
diff --git a/libutils/TEST_MAPPING b/libutils/TEST_MAPPING
new file mode 100644
index 0000000..c8ef45c
--- /dev/null
+++ b/libutils/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libutils_test"
+    }
+  ]
+}
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index 466fbb7..b387d68 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -17,15 +17,16 @@
 #ifndef UTILS_LOOPER_H
 #define UTILS_LOOPER_H
 
-#include <utils/threads.h>
 #include <utils/RefBase.h>
-#include <utils/KeyedVector.h>
 #include <utils/Timers.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
 
 #include <sys/epoll.h>
 
 #include <android-base/unique_fd.h>
 
+#include <unordered_map>
 #include <utility>
 
 namespace android {
@@ -421,18 +422,20 @@
     static sp<Looper> getForThread();
 
 private:
-    struct Request {
-        int fd;
-        int ident;
-        int events;
-        int seq;
-        sp<LooperCallback> callback;
-        void* data;
+  using SequenceNumber = uint64_t;
 
-        void initEventItem(struct epoll_event* eventItem) const;
-    };
+  struct Request {
+      int fd;
+      int ident;
+      int events;
+      sp<LooperCallback> callback;
+      void* data;
+
+      uint32_t getEpollEvents() const;
+  };
 
     struct Response {
+        SequenceNumber seq;
         int events;
         Request request;
     };
@@ -463,9 +466,14 @@
     android::base::unique_fd mEpollFd;  // guarded by mLock but only modified on the looper thread
     bool mEpollRebuildRequired; // guarded by mLock
 
-    // Locked list of file descriptor monitoring requests.
-    KeyedVector<int, Request> mRequests;  // guarded by mLock
-    int mNextRequestSeq;
+    // Locked maps of fds and sequence numbers monitoring requests.
+    // Both maps must be kept in sync at all times.
+    std::unordered_map<SequenceNumber, Request> mRequests;               // guarded by mLock
+    std::unordered_map<int /*fd*/, SequenceNumber> mSequenceNumberByFd;  // guarded by mLock
+
+    // The sequence number to use for the next fd that is added to the looper.
+    // The sequence number 0 is reserved for the WakeEventFd.
+    SequenceNumber mNextRequestSeq;  // guarded by mLock
 
     // This state is only used privately by pollOnce and does not require a lock since
     // it runs on a single thread.
@@ -474,9 +482,8 @@
     nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
 
     int pollInner(int timeoutMillis);
-    int removeFd(int fd, int seq);
+    int removeSequenceNumberLocked(SequenceNumber seq);  // requires mLock
     void awoken();
-    void pushResponse(int events, const Request& request);
     void rebuildEpollLocked();
     void scheduleEpollRebuildLocked();
 
diff --git a/property_service/TEST_MAPPING b/property_service/TEST_MAPPING
new file mode 100644
index 0000000..fcdc86a
--- /dev/null
+++ b/property_service/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "propertyinfoserializer_tests"
+    }
+  ]
+}
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 6861456..87646f9 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -18,7 +18,11 @@
         "-Werror",
     ],
     stl: "none",
-    system_shared_libs: [],
-    header_libs: ["libc_headers"],
+    target: {
+        bionic: {
+            system_shared_libs: [],
+            header_libs: ["libc_headers"],
+        },
+    },
     export_include_dirs: ["include"],
 }
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a7322fa..3831693 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -591,9 +591,23 @@
     # Load trusted keys from dm-verity protected partitions
     exec -- /system/bin/fsverity_init --load-verified-keys
 
+# Only enable the bootreceiver tracing instance for kernels 5.10 and above.
+on late-fs && property:ro.kernel.version=4.9
+    setprop bootreceiver.enable 0
+on late-fs && property:ro.kernel.version=4.14
+    setprop bootreceiver.enable 0
+on late-fs && property:ro.kernel.version=4.19
+    setprop bootreceiver.enable 0
+on late-fs && property:ro.kernel.version=5.4
+    setprop bootreceiver.enable 0
+on late-fs
+    # Bootreceiver tracing instance is enabled by default.
+    setprop bootreceiver.enable ${bootreceiver.enable:-1}
+
+on property:ro.product.cpu.abilist64=* && property:bootreceiver.enable=1
     # 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
@@ -775,11 +789,13 @@
     # Create directories to push tests to for each linker namespace.
     # Create the subdirectories in case the first test is run as root
     # so it doesn't end up owned by root.
-    mkdir /data/local/tests 0700 shell shell
-    mkdir /data/local/tests/product 0700 shell shell
-    mkdir /data/local/tests/system 0700 shell shell
-    mkdir /data/local/tests/unrestricted 0700 shell shell
-    mkdir /data/local/tests/vendor 0700 shell shell
+    # Set directories to be executable by any process so that debuggerd,
+    # aka crash_dump, can read any executables/shared libraries.
+    mkdir /data/local/tests 0701 shell shell
+    mkdir /data/local/tests/product 0701 shell shell
+    mkdir /data/local/tests/system 0701 shell shell
+    mkdir /data/local/tests/unrestricted 0701 shell shell
+    mkdir /data/local/tests/vendor 0701 shell shell
 
     # create dalvik-cache, so as to enforce our permissions
     mkdir /data/dalvik-cache 0771 root root encryption=Require
@@ -1137,7 +1153,7 @@
 # and chown/chmod does not work for /proc/sys/ entries.
 # So proxy writes through init.
 on property:sys.sysctl.extra_free_kbytes=*
-    write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
+    exec -- /system/bin/extra_free_kbytes.sh ${sys.sysctl.extra_free_kbytes}
 
 # Allow users to drop caches
 on property:perf.drop_caches=3
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 9d5cb48..b557dee 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -32,6 +32,7 @@
         "libprotobuf-cpp-lite",
         "libutils",
         "libz",
+        "packagemanager_aidl-cpp",
     ],
 
     cflags: [
diff --git a/toolbox/generate-input.h-labels.py b/toolbox/generate-input.h-labels.py
index c0e9fce..20db638 100755
--- a/toolbox/generate-input.h-labels.py
+++ b/toolbox/generate-input.h-labels.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2015 The Android Open Source Project
 #
@@ -16,7 +16,7 @@
 #
 # pylint: disable=bad-indentation,bad-continuation
 
-from __future__ import print_function
+
 import os
 import re
 import sys
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index e4d9b39..c72af40 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -245,6 +245,8 @@
     tipc_fd = tipc_connect(dev_name, APPLOADER_PORT);
     if (tipc_fd < 0) {
         LOG(ERROR) << "Failed to connect to Trusty app loader: " << strerror(-tipc_fd);
+        // print this to stderr too to avoid silently exiting when run as non-root
+        fprintf(stderr, "Failed to connect to Trusty app loader: %s\n", strerror(-tipc_fd));
         rc = tipc_fd;
         goto err_tipc_connect;
     }
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index ff6460d..7bd1d10 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",
@@ -120,6 +120,7 @@
     ],
     required: [
         "android.hardware.hardware_keystore.xml",
+        "RemoteProvisioner",
     ],
 }
 
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
index 0956fe6..2d44009 100644
--- a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -25,6 +25,8 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <variant>
+#include <vector>
 
 #include <log/log.h>
 #include <trusty/tipc.h>
@@ -46,8 +48,27 @@
     return 0;
 }
 
-int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
-                          uint32_t* out_size) {
+class VectorEraser {
+  public:
+    VectorEraser(std::vector<uint8_t>* v) : _v(v) {}
+    ~VectorEraser() {
+        if (_v) {
+            std::fill(const_cast<volatile uint8_t*>(_v->data()),
+                      const_cast<volatile uint8_t*>(_v->data() + _v->size()), 0);
+        }
+    }
+    void disarm() { _v = nullptr; }
+    VectorEraser(const VectorEraser&) = delete;
+    VectorEraser& operator=(const VectorEraser&) = delete;
+    VectorEraser(VectorEraser&& other) = delete;
+    VectorEraser& operator=(VectorEraser&&) = delete;
+
+  private:
+    std::vector<uint8_t>* _v;
+};
+
+std::variant<int, std::vector<uint8_t>> trusty_keymaster_call_2(uint32_t cmd, void* in,
+                                                                uint32_t in_size) {
     if (handle_ < 0) {
         ALOGE("not connected\n");
         return -EINVAL;
@@ -70,15 +91,38 @@
         ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
         return -errno;
     }
-    size_t out_max_size = *out_size;
-    *out_size = 0;
+
+    std::vector<uint8_t> out(TRUSTY_KEYMASTER_RECV_BUF_SIZE);
+    VectorEraser out_eraser(&out);
+    uint8_t* write_pos = out.data();
+    uint8_t* out_end = out.data() + out.size();
+
     struct iovec iov[2];
     struct keymaster_message header;
     iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};
     while (true) {
-        iov[1] = {.iov_base = out + *out_size,
-                  .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH,
-                                                out_max_size - *out_size)};
+        if (out_end - write_pos < KEYMASTER_MAX_BUFFER_LENGTH) {
+            // In stead of using std::vector.resize(), allocate a new one to have chance
+            // at zeroing the old buffer.
+            std::vector<uint8_t> new_out(out.size() + KEYMASTER_MAX_BUFFER_LENGTH);
+            // After the swap below this erases the old out buffer.
+            VectorEraser new_out_eraser(&new_out);
+            std::copy(out.data(), write_pos, new_out.begin());
+
+            auto write_offset = write_pos - out.data();
+
+            std::swap(new_out, out);
+
+            write_pos = out.data() + write_offset;
+            out_end = out.data() + out.size();
+        }
+        size_t buffer_size = 0;
+        if (__builtin_sub_overflow(reinterpret_cast<uintptr_t>(out_end),
+                                   reinterpret_cast<uintptr_t>(write_pos), &buffer_size)) {
+            return -EOVERFLOW;
+        }
+        iov[1] = {.iov_base = write_pos, .iov_len = buffer_size};
+
         rc = readv(handle_, iov, 2);
         if (rc < 0) {
             ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
@@ -95,13 +139,36 @@
             ALOGE("invalid command (%d)", header.cmd);
             return -EINVAL;
         }
-        *out_size += ((size_t)rc - sizeof(struct keymaster_message));
+        write_pos += ((size_t)rc - sizeof(struct keymaster_message));
         if (header.cmd & KEYMASTER_STOP_BIT) {
             break;
         }
     }
 
-    return rc;
+    out.resize(write_pos - out.data());
+    out_eraser.disarm();
+    return out;
+}
+
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+                          uint32_t* out_size) {
+    auto result = trusty_keymaster_call_2(cmd, in, in_size);
+    if (auto out_buffer = std::get_if<std::vector<uint8_t>>(&result)) {
+        if (out_buffer->size() <= *out_size) {
+            std::copy(out_buffer->begin(), out_buffer->end(), out);
+            std::fill(const_cast<volatile uint8_t*>(&*out_buffer->begin()),
+                      const_cast<volatile uint8_t*>(&*out_buffer->end()), 0);
+
+            *out_size = out_buffer->size();
+            return 0;
+        } else {
+            ALOGE("Message was to large (%zu) for the provided buffer (%u)", out_buffer->size(),
+                  *out_size);
+            return -EMSGSIZE;
+        }
+    } else {
+        return std::get<int>(result);
+    }
 }
 
 void trusty_keymaster_disconnect() {
@@ -155,28 +222,27 @@
     req.Serialize(send_buf, send_buf + req_size);
 
     // Send it
-    uint8_t recv_buf[TRUSTY_KEYMASTER_RECV_BUF_SIZE];
-    keymaster::Eraser recv_buf_eraser(recv_buf, TRUSTY_KEYMASTER_RECV_BUF_SIZE);
-    uint32_t rsp_size = TRUSTY_KEYMASTER_RECV_BUF_SIZE;
-    int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
-    if (rc < 0) {
+    auto response = trusty_keymaster_call_2(command, send_buf, req_size);
+    if (auto response_buffer = std::get_if<std::vector<uint8_t>>(&response)) {
+        keymaster::Eraser response_buffer_erasor(response_buffer->data(), response_buffer->size());
+        ALOGV("Received %zu byte response\n", response_buffer->size());
+
+        const uint8_t* p = response_buffer->data();
+        if (!rsp->Deserialize(&p, p + response_buffer->size())) {
+            ALOGE("Error deserializing response of size %zu\n", response_buffer->size());
+            return KM_ERROR_UNKNOWN_ERROR;
+        } else if (rsp->error != KM_ERROR_OK) {
+            ALOGE("Response of size %zu contained error code %d\n", response_buffer->size(),
+                  (int)rsp->error);
+        }
+        return rsp->error;
+    } else {
+        auto rc = std::get<int>(response);
         // Reset the connection on tipc error
         trusty_keymaster_disconnect();
         trusty_keymaster_connect();
         ALOGE("tipc error: %d\n", rc);
         // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
         return translate_error(rc);
-    } else {
-        ALOGV("Received %d byte response\n", rsp_size);
     }
-
-    const uint8_t* p = recv_buf;
-    if (!rsp->Deserialize(&p, p + rsp_size)) {
-        ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
-        return KM_ERROR_UNKNOWN_ERROR;
-    } else if (rsp->error != KM_ERROR_OK) {
-        ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error);
-        return rsp->error;
-    }
-    return rsp->error;
 }
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 29c6f93..eb0acb5 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -45,36 +45,40 @@
 static const char *main_ctrl_name = "com.android.ipc-unittest.ctrl";
 static const char* receiver_name = "com.android.trusty.memref.receiver";
 
-static const char *_sopts = "hsvD:t:r:m:b:";
+static const char* _sopts = "hsvDS:t:r:m:b:";
+/* clang-format off */
 static const struct option _lopts[] =  {
-	{"help",    no_argument,       0, 'h'},
-	{"silent",  no_argument,       0, 's'},
-	{"variable",no_argument,       0, 'v'},
-	{"dev",     required_argument, 0, 'D'},
-	{"repeat",  required_argument, 0, 'r'},
-	{"burst",   required_argument, 0, 'b'},
-	{"msgsize", required_argument, 0, 'm'},
-	{0, 0, 0, 0}
+    {"help",    no_argument,       0, 'h'},
+    {"silent",  no_argument,       0, 's'},
+    {"variable",no_argument,       0, 'v'},
+    {"dev",     required_argument, 0, 'D'},
+    {"srv",     required_argument, 0, 'S'},
+    {"repeat",  required_argument, 0, 'r'},
+    {"burst",   required_argument, 0, 'b'},
+    {"msgsize", required_argument, 0, 'm'},
+    {0, 0, 0, 0}
 };
+/* clang-format on */
 
-static const char *usage =
-"Usage: %s [options]\n"
-"\n"
-"options:\n"
-"  -h, --help            prints this message and exit\n"
-"  -D, --dev name        device name\n"
-"  -t, --test name       test to run\n"
-"  -r, --repeat cnt      repeat count\n"
-"  -m, --msgsize size    max message size\n"
-"  -v, --variable        variable message size\n"
-"  -s, --silent          silent\n"
-"\n"
-;
+static const char* usage =
+        "Usage: %s [options]\n"
+        "\n"
+        "options:\n"
+        "  -h, --help            prints this message and exit\n"
+        "  -D, --dev name        device name\n"
+        "  -S, --srv name        service name\n"
+        "  -t, --test name       test to run\n"
+        "  -r, --repeat cnt      repeat count\n"
+        "  -b, --burst cnt       burst count\n"
+        "  -m, --msgsize size    max message size\n"
+        "  -v, --variable        variable message size\n"
+        "  -s, --silent          silent\n"
+        "\n";
 
 static const char* usage_long =
         "\n"
         "The following tests are available:\n"
-        "   connect      - connect to datasink service\n"
+        "   connect      - connect to specified service, defaults to echo+datasink\n"
         "   connect_foo  - connect to non existing service\n"
         "   burst_write  - send messages to datasink service\n"
         "   echo         - send/receive messages to echo service\n"
@@ -96,798 +100,774 @@
 static uint opt_msgburst = 32;
 static bool opt_variable = false;
 static bool opt_silent = false;
+static char* srv_name = NULL;
 
 static void print_usage_and_exit(const char *prog, int code, bool verbose)
 {
-	fprintf (stderr, usage, prog);
-	if (verbose)
-		fprintf (stderr, "%s", usage_long);
-	exit(code);
+    fprintf(stderr, usage, prog);
+    if (verbose) fprintf(stderr, "%s", usage_long);
+    exit(code);
 }
 
 static void parse_options(int argc, char **argv)
 {
-	int c;
-	int oidx = 0;
+    int c;
+    int oidx = 0;
 
-	while (1)
-	{
-		c = getopt_long (argc, argv, _sopts, _lopts, &oidx);
-		if (c == -1)
-			break; /* done */
+    while (1) {
+        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
+        if (c == -1) break; /* done */
 
-		switch (c) {
+        switch (c) {
+            case 'D':
+                dev_name = strdup(optarg);
+                break;
 
-		case 'D':
-			dev_name = strdup(optarg);
-		break;
+            case 'S':
+                srv_name = strdup(optarg);
+                break;
 
-		case 't':
-			test_name = strdup(optarg);
-		break;
+            case 't':
+                test_name = strdup(optarg);
+                break;
 
-		case 'v':
-			opt_variable = true;
-		break;
+            case 'v':
+                opt_variable = true;
+                break;
 
-		case 'r':
-			opt_repeat = atoi(optarg);
-		break;
+            case 'r':
+                opt_repeat = atoi(optarg);
+                break;
 
-		case 'm':
-			opt_msgsize = atoi(optarg);
-		break;
+            case 'm':
+                opt_msgsize = atoi(optarg);
+                break;
 
-		case 'b':
-			opt_msgburst = atoi(optarg);
-		break;
+            case 'b':
+                opt_msgburst = atoi(optarg);
+                break;
 
-		case 's':
-			opt_silent = true;
-		break;
+            case 's':
+                opt_silent = true;
+                break;
 
-		case 'h':
-		      print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
-		break;
+            case 'h':
+                print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
+                break;
 
-		default:
-		      print_usage_and_exit(argv[0], EXIT_FAILURE, false);
-		}
-	}
+            default:
+                print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+        }
+    }
 }
 
 static int connect_test(uint repeat)
 {
-	uint i;
-	int  echo_fd;
-	int  dsink_fd;
+    uint i;
+    int echo_fd;
+    int dsink_fd;
+    int custom_fd;
 
-	if (!opt_silent) {
-		printf("%s: repeat = %u\n", __func__, repeat);
-	}
+    if (!opt_silent) {
+        printf("%s: repeat = %u\n", __func__, repeat);
+    }
 
-	for (i = 0; i < repeat; i++) {
-		echo_fd = tipc_connect(dev_name, echo_name);
-		if (echo_fd < 0) {
-			fprintf(stderr, "Failed to connect to '%s' service\n",
-				"echo");
-		}
-		dsink_fd = tipc_connect(dev_name, datasink_name);
-		if (dsink_fd < 0) {
-			fprintf(stderr, "Failed to connect to '%s' service\n",
-				"datasink");
-		}
+    for (i = 0; i < repeat; i++) {
+        if (srv_name) {
+            custom_fd = tipc_connect(dev_name, srv_name);
+            if (custom_fd < 0) {
+                fprintf(stderr, "Failed to connect to '%s' service\n", srv_name);
+            }
+            if (custom_fd >= 0) {
+                tipc_close(custom_fd);
+            }
+        } else {
+            echo_fd = tipc_connect(dev_name, echo_name);
+            if (echo_fd < 0) {
+                fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
+            }
+            dsink_fd = tipc_connect(dev_name, datasink_name);
+            if (dsink_fd < 0) {
+                fprintf(stderr, "Failed to connect to '%s' service\n", "datasink");
+            }
 
-		if (echo_fd >= 0) {
-			tipc_close(echo_fd);
-		}
-		if (dsink_fd >= 0) {
-			tipc_close(dsink_fd);
-		}
-	}
+            if (echo_fd >= 0) {
+                tipc_close(echo_fd);
+            }
+            if (dsink_fd >= 0) {
+                tipc_close(dsink_fd);
+            }
+        }
+    }
 
-	if (!opt_silent) {
-		printf("%s: done\n", __func__);
-	}
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	return 0;
+    return 0;
 }
 
 static int connect_foo(uint repeat)
 {
-	uint i;
-	int  fd;
+    uint i;
+    int fd;
 
-	if (!opt_silent) {
-		printf("%s: repeat = %u\n", __func__, repeat);
-	}
+    if (!opt_silent) {
+        printf("%s: repeat = %u\n", __func__, repeat);
+    }
 
-	for (i = 0; i < repeat; i++) {
-		fd = tipc_connect(dev_name, "foo");
-		if (fd >= 0) {
-			fprintf(stderr, "succeeded to connect to '%s' service\n",
-				"foo");
-			tipc_close(fd);
-		}
-	}
+    for (i = 0; i < repeat; i++) {
+        fd = tipc_connect(dev_name, "foo");
+        if (fd >= 0) {
+            fprintf(stderr, "succeeded to connect to '%s' service\n", "foo");
+            tipc_close(fd);
+        }
+    }
 
-	if (!opt_silent) {
-		printf("%s: done\n", __func__);
-	}
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	return 0;
+    return 0;
 }
 
 
 static int closer1_test(uint repeat)
 {
-	uint i;
-	int  fd;
+    uint i;
+    int fd;
 
-	if (!opt_silent) {
-		printf("%s: repeat = %u\n", __func__, repeat);
-	}
+    if (!opt_silent) {
+        printf("%s: repeat = %u\n", __func__, repeat);
+    }
 
-	for (i = 0; i < repeat; i++) {
-		fd = tipc_connect(dev_name, closer1_name);
-		if (fd < 0) {
-			fprintf(stderr, "Failed to connect to '%s' service\n",
-				"closer1");
-			continue;
-		}
-		if (!opt_silent) {
-			printf("%s: connected\n", __func__);
-		}
-		tipc_close(fd);
-	}
+    for (i = 0; i < repeat; i++) {
+        fd = tipc_connect(dev_name, closer1_name);
+        if (fd < 0) {
+            fprintf(stderr, "Failed to connect to '%s' service\n", "closer1");
+            continue;
+        }
+        if (!opt_silent) {
+            printf("%s: connected\n", __func__);
+        }
+        tipc_close(fd);
+    }
 
-	if (!opt_silent) {
-		printf("%s: done\n", __func__);
-	}
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	return 0;
+    return 0;
 }
 
 static int closer2_test(uint repeat)
 {
-	uint i;
-	int  fd;
+    uint i;
+    int fd;
 
-	if (!opt_silent) {
-		printf("%s: repeat = %u\n", __func__, repeat);
-	}
+    if (!opt_silent) {
+        printf("%s: repeat = %u\n", __func__, repeat);
+    }
 
-	for (i = 0; i < repeat; i++) {
-		fd = tipc_connect(dev_name, closer2_name);
-		if (fd < 0) {
-			if (!opt_silent) {
-				printf("failed to connect to '%s' service\n", "closer2");
-			}
-		} else {
-			/* this should always fail */
-			fprintf(stderr, "connected to '%s' service\n", "closer2");
-			tipc_close(fd);
-		}
-	}
+    for (i = 0; i < repeat; i++) {
+        fd = tipc_connect(dev_name, closer2_name);
+        if (fd < 0) {
+            if (!opt_silent) {
+                printf("failed to connect to '%s' service\n", "closer2");
+            }
+        } else {
+            /* this should always fail */
+            fprintf(stderr, "connected to '%s' service\n", "closer2");
+            tipc_close(fd);
+        }
+    }
 
-	if (!opt_silent) {
-		printf("%s: done\n", __func__);
-	}
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	return 0;
+    return 0;
 }
 
 static int closer3_test(uint repeat)
 {
-	uint i, j;
-	ssize_t rc;
-	int  fd[4];
-	char buf[64];
+    uint i, j;
+    ssize_t rc;
+    int fd[4];
+    char buf[64];
 
-	if (!opt_silent) {
-		printf("%s: repeat = %u\n", __func__, repeat);
-	}
+    if (!opt_silent) {
+        printf("%s: repeat = %u\n", __func__, repeat);
+    }
 
-	for (i = 0; i < repeat; i++) {
+    for (i = 0; i < repeat; i++) {
+        /* open 4 connections to closer3 service */
+        for (j = 0; j < 4; j++) {
+            fd[j] = tipc_connect(dev_name, closer3_name);
+            if (fd[j] < 0) {
+                fprintf(stderr, "fd[%d]: failed to connect to '%s' service\n", j, "closer3");
+            } else {
+                if (!opt_silent) {
+                    printf("%s: fd[%d]=%d: connected\n", __func__, j, fd[j]);
+                }
+                memset(buf, i + j, sizeof(buf));
+                rc = write(fd[j], buf, sizeof(buf));
+                if (rc != sizeof(buf)) {
+                    if (!opt_silent) {
+                        printf("%s: fd[%d]=%d: write returned  = %zd\n", __func__, j, fd[j], rc);
+                    }
+                    perror("closer3_test: write");
+                }
+            }
+        }
 
-		/* open 4 connections to closer3 service */
-		for (j = 0; j < 4; j++) {
-			fd[j] = tipc_connect(dev_name, closer3_name);
-			if (fd[j] < 0) {
-				fprintf(stderr, "fd[%d]: failed to connect to '%s' service\n", j, "closer3");
-			} else {
-				if (!opt_silent) {
-					printf("%s: fd[%d]=%d: connected\n", __func__, j, fd[j]);
-				}
-				memset(buf, i + j, sizeof(buf));
-				rc = write(fd[j], buf, sizeof(buf));
-				if (rc != sizeof(buf)) {
-					if (!opt_silent) {
-						printf("%s: fd[%d]=%d: write returned  = %zd\n",
-							__func__, j, fd[j], rc);
-					}
-					perror("closer3_test: write");
-				}
-			}
-		}
+        /* sleep a bit */
+        sleep(1);
 
-		/* sleep a bit */
-		sleep(1);
+        /* It is expected that they will be closed by remote */
+        for (j = 0; j < 4; j++) {
+            if (fd[j] < 0) continue;
+            rc = write(fd[j], buf, sizeof(buf));
+            if (rc != sizeof(buf)) {
+                if (!opt_silent) {
+                    printf("%s: fd[%d]=%d: write returned = %zd\n", __func__, j, fd[j], rc);
+                }
+                perror("closer3_test: write");
+            }
+        }
 
-		/* It is expected that they will be closed by remote */
-		for (j = 0; j < 4; j++) {
-			if (fd[j] < 0)
-				continue;
-			rc = write(fd[j], buf, sizeof(buf));
-			if (rc != sizeof(buf)) {
-				if (!opt_silent) {
-					printf("%s: fd[%d]=%d: write returned = %zd\n",
-						__func__, j, fd[j], rc);
-				}
-				perror("closer3_test: write");
-			}
-		}
+        /* then they have to be closed by remote */
+        for (j = 0; j < 4; j++) {
+            if (fd[j] >= 0) {
+                tipc_close(fd[j]);
+            }
+        }
+    }
 
-		/* then they have to be closed by remote */
-		for (j = 0; j < 4; j++) {
-			if (fd[j] >= 0) {
-				tipc_close(fd[j]);
-			}
-		}
-	}
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	if (!opt_silent) {
-		printf("%s: done\n", __func__);
-	}
-
-	return 0;
+    return 0;
 }
 
 
 static int echo_test(uint repeat, uint msgsz, bool var)
 {
-	uint i;
-	ssize_t rc;
-	size_t  msg_len;
-	int  echo_fd =-1;
-	char tx_buf[msgsz];
-	char rx_buf[msgsz];
+    uint i;
+    ssize_t rc;
+    size_t msg_len;
+    int echo_fd = -1;
+    char tx_buf[msgsz];
+    char rx_buf[msgsz];
 
-	if (!opt_silent) {
-		printf("%s: repeat %u: msgsz %u: variable %s\n",
-			__func__, repeat, msgsz, var ? "true" : "false");
-	}
+    if (!opt_silent) {
+        printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
+               var ? "true" : "false");
+    }
 
-	echo_fd = tipc_connect(dev_name, echo_name);
-	if (echo_fd < 0) {
-		fprintf(stderr, "Failed to connect to service\n");
-		return echo_fd;
-	}
+    echo_fd = tipc_connect(dev_name, echo_name);
+    if (echo_fd < 0) {
+        fprintf(stderr, "Failed to connect to service\n");
+        return echo_fd;
+    }
 
-	for (i = 0; i < repeat; i++) {
+    for (i = 0; i < repeat; i++) {
+        msg_len = msgsz;
+        if (opt_variable && msgsz) {
+            msg_len = rand() % msgsz;
+        }
 
-		msg_len = msgsz;
-		if (opt_variable && msgsz) {
-			msg_len = rand() % msgsz;
-		}
+        memset(tx_buf, i + 1, msg_len);
 
-		memset(tx_buf, i + 1, msg_len);
+        rc = write(echo_fd, tx_buf, msg_len);
+        if ((size_t)rc != msg_len) {
+            perror("echo_test: write");
+            break;
+        }
 
-		rc = write(echo_fd, tx_buf, msg_len);
-		if ((size_t)rc != msg_len) {
-			perror("echo_test: write");
-			break;
-		}
+        rc = read(echo_fd, rx_buf, msg_len);
+        if (rc < 0) {
+            perror("echo_test: read");
+            break;
+        }
 
-		rc = read(echo_fd, rx_buf, msg_len);
-		if (rc < 0) {
-			perror("echo_test: read");
-			break;
-		}
+        if ((size_t)rc != msg_len) {
+            fprintf(stderr, "data truncated (%zu vs. %zu)\n", rc, msg_len);
+            continue;
+        }
 
-		if ((size_t)rc != msg_len) {
-			fprintf(stderr, "data truncated (%zu vs. %zu)\n",
-			                 rc, msg_len);
-			continue;
-		}
+        if (memcmp(tx_buf, rx_buf, (size_t)rc)) {
+            fprintf(stderr, "data mismatch\n");
+            continue;
+        }
+    }
 
-		if (memcmp(tx_buf, rx_buf, (size_t) rc)) {
-			fprintf(stderr, "data mismatch\n");
-			continue;
-		}
-	}
+    tipc_close(echo_fd);
 
-	tipc_close(echo_fd);
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	if (!opt_silent) {
-		printf("%s: done\n",__func__);
-	}
-
-	return 0;
+    return 0;
 }
 
 static int burst_write_test(uint repeat, uint msgburst, uint msgsz, bool var)
 {
-	int fd;
-	uint i, j;
-	ssize_t rc;
-	size_t  msg_len;
-	char tx_buf[msgsz];
+    int fd;
+    uint i, j;
+    ssize_t rc;
+    size_t msg_len;
+    char tx_buf[msgsz];
 
-	if (!opt_silent) {
-		printf("%s: repeat %u: burst %u: msgsz %u: variable %s\n",
-			__func__, repeat, msgburst, msgsz,
-			var ? "true" : "false");
-	}
+    if (!opt_silent) {
+        printf("%s: repeat %u: burst %u: msgsz %u: variable %s\n", __func__, repeat, msgburst,
+               msgsz, var ? "true" : "false");
+    }
 
-	for (i = 0; i < repeat; i++) {
+    for (i = 0; i < repeat; i++) {
+        fd = tipc_connect(dev_name, datasink_name);
+        if (fd < 0) {
+            fprintf(stderr, "Failed to connect to '%s' service\n", "datasink");
+            break;
+        }
 
-		fd = tipc_connect(dev_name, datasink_name);
-		if (fd < 0) {
-			fprintf(stderr, "Failed to connect to '%s' service\n",
-				"datasink");
-			break;
-		}
+        for (j = 0; j < msgburst; j++) {
+            msg_len = msgsz;
+            if (var && msgsz) {
+                msg_len = rand() % msgsz;
+            }
 
-		for (j = 0; j < msgburst; j++) {
-			msg_len = msgsz;
-			if (var && msgsz) {
-				msg_len = rand() % msgsz;
-			}
+            memset(tx_buf, i + 1, msg_len);
+            rc = write(fd, tx_buf, msg_len);
+            if ((size_t)rc != msg_len) {
+                perror("burst_test: write");
+                break;
+            }
+        }
 
-			memset(tx_buf, i + 1, msg_len);
-			rc = write(fd, tx_buf, msg_len);
-			if ((size_t)rc != msg_len) {
-				perror("burst_test: write");
-				break;
-			}
-		}
+        tipc_close(fd);
+    }
 
-		tipc_close(fd);
-	}
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	if (!opt_silent) {
-		printf("%s: done\n",__func__);
-	}
-
-	return 0;
+    return 0;
 }
 
 
 static int _wait_for_msg(int fd, uint msgsz, int timeout)
 {
-	int rc;
-	fd_set rfds;
-	uint msgcnt = 0;
-	char rx_buf[msgsz];
-	struct timeval tv;
+    int rc;
+    fd_set rfds;
+    uint msgcnt = 0;
+    char rx_buf[msgsz];
+    struct timeval tv;
 
-	if (!opt_silent) {
-		printf("waiting (%d) for msg\n", timeout);
-	}
+    if (!opt_silent) {
+        printf("waiting (%d) for msg\n", timeout);
+    }
 
-	FD_ZERO(&rfds);
-	FD_SET(fd, &rfds);
+    FD_ZERO(&rfds);
+    FD_SET(fd, &rfds);
 
-	tv.tv_sec = timeout;
-	tv.tv_usec = 0;
+    tv.tv_sec = timeout;
+    tv.tv_usec = 0;
 
-	for(;;) {
-		rc = select(fd+1, &rfds, NULL, NULL, &tv);
+    for (;;) {
+        rc = select(fd + 1, &rfds, NULL, NULL, &tv);
 
-		if (rc == 0) {
-			if (!opt_silent) {
-				printf("select timedout\n");
-			}
-			break;
-		}
+        if (rc == 0) {
+            if (!opt_silent) {
+                printf("select timedout\n");
+            }
+            break;
+        }
 
-		if (rc == -1) {
-			perror("select_test: select");
-			return rc;
-		}
+        if (rc == -1) {
+            perror("select_test: select");
+            return rc;
+        }
 
-		rc = read(fd, rx_buf, sizeof(rx_buf));
-		if (rc < 0) {
-			perror("select_test: read");
-			return rc;
-		} else {
-			if (rc > 0) {
-				msgcnt++;
-			}
-		}
-	}
+        rc = read(fd, rx_buf, sizeof(rx_buf));
+        if (rc < 0) {
+            perror("select_test: read");
+            return rc;
+        } else {
+            if (rc > 0) {
+                msgcnt++;
+            }
+        }
+    }
 
-	if (!opt_silent) {
-		printf("got %u messages\n", msgcnt);
-	}
+    if (!opt_silent) {
+        printf("got %u messages\n", msgcnt);
+    }
 
-	return 0;
+    return 0;
 }
 
 
 static int select_test(uint repeat, uint msgburst, uint msgsz)
 {
-	int fd;
-	uint i, j;
-	ssize_t rc;
-	char tx_buf[msgsz];
+    int fd;
+    uint i, j;
+    ssize_t rc;
+    char tx_buf[msgsz];
 
-	if (!opt_silent) {
-		printf("%s: repeat %u\n", __func__, repeat);
-	}
+    if (!opt_silent) {
+        printf("%s: repeat %u\n", __func__, repeat);
+    }
 
-	fd = tipc_connect(dev_name, echo_name);
-	if (fd < 0) {
-		fprintf(stderr, "Failed to connect to '%s' service\n",
-			"echo");
-		return fd;
-	}
+    fd = tipc_connect(dev_name, echo_name);
+    if (fd < 0) {
+        fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
+        return fd;
+    }
 
-	for (i = 0; i < repeat; i++) {
+    for (i = 0; i < repeat; i++) {
+        _wait_for_msg(fd, msgsz, 1);
 
-		_wait_for_msg(fd, msgsz, 1);
+        if (!opt_silent) {
+            printf("sending burst: %u msg\n", msgburst);
+        }
 
-		if (!opt_silent) {
-			printf("sending burst: %u msg\n", msgburst);
-		}
+        for (j = 0; j < msgburst; j++) {
+            memset(tx_buf, i + j, msgsz);
+            rc = write(fd, tx_buf, msgsz);
+            if ((size_t)rc != msgsz) {
+                perror("burst_test: write");
+                break;
+            }
+        }
+    }
 
-		for (j = 0; j < msgburst; j++) {
-			memset(tx_buf, i + j, msgsz);
-			rc = write(fd, tx_buf, msgsz);
-			if ((size_t)rc != msgsz) {
-				perror("burst_test: write");
-				break;
-			}
-		}
-	}
+    tipc_close(fd);
 
-	tipc_close(fd);
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	if (!opt_silent) {
-		printf("%s: done\n",__func__);
-	}
-
-	return 0;
+    return 0;
 }
 
 static int blocked_read_test(uint repeat)
 {
-	int fd;
-	uint i;
-	ssize_t rc;
-	char rx_buf[512];
+    int fd;
+    uint i;
+    ssize_t rc;
+    char rx_buf[512];
 
-	if (!opt_silent) {
-		printf("%s: repeat %u\n", __func__, repeat);
-	}
+    if (!opt_silent) {
+        printf("%s: repeat %u\n", __func__, repeat);
+    }
 
-	fd = tipc_connect(dev_name, echo_name);
-	if (fd < 0) {
-		fprintf(stderr, "Failed to connect to '%s' service\n",
-			"echo");
-		return fd;
-	}
+    fd = tipc_connect(dev_name, echo_name);
+    if (fd < 0) {
+        fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
+        return fd;
+    }
 
-	for (i = 0; i < repeat; i++) {
-		rc = read(fd, rx_buf, sizeof(rx_buf));
-		if (rc < 0) {
-			perror("select_test: read");
-			break;
-		} else {
-			if (!opt_silent) {
-				printf("got %zd bytes\n", rc);
-			}
-		}
-	}
+    for (i = 0; i < repeat; i++) {
+        rc = read(fd, rx_buf, sizeof(rx_buf));
+        if (rc < 0) {
+            perror("select_test: read");
+            break;
+        } else {
+            if (!opt_silent) {
+                printf("got %zd bytes\n", rc);
+            }
+        }
+    }
 
-	tipc_close(fd);
+    tipc_close(fd);
 
-	if (!opt_silent) {
-		printf("%s: done\n",__func__);
-	}
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	return 0;
+    return 0;
 }
 
 static int ta2ta_ipc_test(void)
 {
-	enum test_message_header {
-		TEST_PASSED = 0,
-		TEST_FAILED = 1,
-		TEST_MESSAGE = 2,
-	};
+    enum test_message_header {
+        TEST_PASSED = 0,
+        TEST_FAILED = 1,
+        TEST_MESSAGE = 2,
+    };
 
-	int fd;
-	int ret;
-	unsigned char rx_buf[256];
+    int fd;
+    int ret;
+    unsigned char rx_buf[256];
 
-	if (!opt_silent) {
-		printf("%s:\n", __func__);
-	}
+    if (!opt_silent) {
+        printf("%s:\n", __func__);
+    }
 
-	fd = tipc_connect(dev_name, main_ctrl_name);
-	if (fd < 0) {
-		fprintf(stderr, "Failed to connect to '%s' service\n",
-			"main_ctrl");
-		return fd;
-	}
+    fd = tipc_connect(dev_name, main_ctrl_name);
+    if (fd < 0) {
+        fprintf(stderr, "Failed to connect to '%s' service\n", "main_ctrl");
+        return fd;
+    }
 
-	/* Wait for tests to complete and read status */
-	while (true) {
-		ret = read(fd, rx_buf, sizeof(rx_buf));
-		if (ret <= 0 || ret >= (int)sizeof(rx_buf)) {
-			fprintf(stderr, "%s: Read failed: %d\n", __func__, ret);
-			tipc_close(fd);
-			return -1;
-		}
+    /* Wait for tests to complete and read status */
+    while (true) {
+        ret = read(fd, rx_buf, sizeof(rx_buf));
+        if (ret <= 0 || ret >= (int)sizeof(rx_buf)) {
+            fprintf(stderr, "%s: Read failed: %d\n", __func__, ret);
+            tipc_close(fd);
+            return -1;
+        }
 
-		if (rx_buf[0] == TEST_PASSED) {
-			break;
-		} else if (rx_buf[0] == TEST_FAILED) {
-			break;
-		} else if (rx_buf[0] == TEST_MESSAGE) {
-			write(STDOUT_FILENO, rx_buf + 1, ret - 1);
-		} else {
-			fprintf(stderr, "%s: Bad message header: %d\n",
-			        __func__, rx_buf[0]);
-			break;
-		}
-	}
+        if (rx_buf[0] == TEST_PASSED) {
+            break;
+        } else if (rx_buf[0] == TEST_FAILED) {
+            break;
+        } else if (rx_buf[0] == TEST_MESSAGE) {
+            write(STDOUT_FILENO, rx_buf + 1, ret - 1);
+        } else {
+            fprintf(stderr, "%s: Bad message header: %d\n", __func__, rx_buf[0]);
+            break;
+        }
+    }
 
-	tipc_close(fd);
+    tipc_close(fd);
 
-	return rx_buf[0] == TEST_PASSED ? 0 : -1;
+    return rx_buf[0] == TEST_PASSED ? 0 : -1;
 }
 
 typedef struct uuid
 {
-	uint32_t time_low;
-	uint16_t time_mid;
-	uint16_t time_hi_and_version;
-	uint8_t clock_seq_and_node[8];
+    uint32_t time_low;
+    uint16_t time_mid;
+    uint16_t time_hi_and_version;
+    uint8_t clock_seq_and_node[8];
 } uuid_t;
 
 static void print_uuid(const char *dev, uuid_t *uuid)
 {
-	printf("%s:", dev);
-	printf("uuid: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
-	       uuid->time_low,
-	       uuid->time_mid,
-	       uuid->time_hi_and_version,
-	       uuid->clock_seq_and_node[0],
-	       uuid->clock_seq_and_node[1],
-	       uuid->clock_seq_and_node[2],
-	       uuid->clock_seq_and_node[3],
-	       uuid->clock_seq_and_node[4],
-	       uuid->clock_seq_and_node[5],
-	       uuid->clock_seq_and_node[6],
-	       uuid->clock_seq_and_node[7]
-	       );
+    printf("%s:", dev);
+    printf("uuid: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", uuid->time_low,
+           uuid->time_mid, uuid->time_hi_and_version, uuid->clock_seq_and_node[0],
+           uuid->clock_seq_and_node[1], uuid->clock_seq_and_node[2], uuid->clock_seq_and_node[3],
+           uuid->clock_seq_and_node[4], uuid->clock_seq_and_node[5], uuid->clock_seq_and_node[6],
+           uuid->clock_seq_and_node[7]);
 }
 
 static int dev_uuid_test(void)
 {
-	int fd;
-	ssize_t rc;
-	uuid_t uuid;
+    int fd;
+    ssize_t rc;
+    uuid_t uuid;
 
-	fd = tipc_connect(dev_name, uuid_name);
-	if (fd < 0) {
-		fprintf(stderr, "Failed to connect to '%s' service\n",
-			"uuid");
-		return fd;
-	}
+    fd = tipc_connect(dev_name, uuid_name);
+    if (fd < 0) {
+        fprintf(stderr, "Failed to connect to '%s' service\n", "uuid");
+        return fd;
+    }
 
-	/* wait for test to complete */
-	rc = read(fd, &uuid, sizeof(uuid));
-	if (rc < 0) {
-		perror("dev_uuid_test: read");
-	} else if (rc != sizeof(uuid)) {
-		fprintf(stderr, "unexpected uuid size (%d vs. %d)\n",
-			(int)rc, (int)sizeof(uuid));
-	} else {
-		print_uuid(dev_name, &uuid);
-	}
+    /* wait for test to complete */
+    rc = read(fd, &uuid, sizeof(uuid));
+    if (rc < 0) {
+        perror("dev_uuid_test: read");
+    } else if (rc != sizeof(uuid)) {
+        fprintf(stderr, "unexpected uuid size (%d vs. %d)\n", (int)rc, (int)sizeof(uuid));
+    } else {
+        print_uuid(dev_name, &uuid);
+    }
 
-	tipc_close(fd);
+    tipc_close(fd);
 
-	return 0;
+    return 0;
 }
 
 static int ta_access_test(void)
 {
-	int fd;
+    int fd;
 
-	if (!opt_silent) {
-		printf("%s:\n", __func__);
-	}
+    if (!opt_silent) {
+        printf("%s:\n", __func__);
+    }
 
-	fd = tipc_connect(dev_name, ta_only_name);
-	if (fd >= 0) {
-		fprintf(stderr, "Succeed to connect to '%s' service\n",
-			"ta_only");
-		tipc_close(fd);
-	}
+    fd = tipc_connect(dev_name, ta_only_name);
+    if (fd >= 0) {
+        fprintf(stderr, "Succeed to connect to '%s' service\n", "ta_only");
+        tipc_close(fd);
+    }
 
-	fd = tipc_connect(dev_name, ns_only_name);
-	if (fd < 0) {
-		fprintf(stderr, "Failed to connect to '%s' service\n",
-			"ns_only");
-		return fd;
-	}
-	tipc_close(fd);
+    fd = tipc_connect(dev_name, ns_only_name);
+    if (fd < 0) {
+        fprintf(stderr, "Failed to connect to '%s' service\n", "ns_only");
+        return fd;
+    }
+    tipc_close(fd);
 
-	if (!opt_silent) {
-		printf("%s: done\n",__func__);
-	}
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	return 0;
+    return 0;
 }
 
 
 static int writev_test(uint repeat, uint msgsz, bool var)
 {
-	uint i;
-	ssize_t rc;
-	size_t  msg_len;
-	int  echo_fd = -1;
-	char tx0_buf[msgsz];
-	char tx1_buf[msgsz];
-	char rx_buf [msgsz];
-	struct iovec iovs[2]= {{tx0_buf, 0}, {tx1_buf, 0}};
+    uint i;
+    ssize_t rc;
+    size_t msg_len;
+    int echo_fd = -1;
+    char tx0_buf[msgsz];
+    char tx1_buf[msgsz];
+    char rx_buf[msgsz];
+    struct iovec iovs[2] = {{tx0_buf, 0}, {tx1_buf, 0}};
 
-	if (!opt_silent) {
-		printf("%s: repeat %u: msgsz %u: variable %s\n",
-			__func__, repeat, msgsz, var ? "true" : "false");
-	}
+    if (!opt_silent) {
+        printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
+               var ? "true" : "false");
+    }
 
-	echo_fd = tipc_connect(dev_name, echo_name);
-	if (echo_fd < 0) {
-		fprintf(stderr, "Failed to connect to service\n");
-		return echo_fd;
-	}
+    echo_fd = tipc_connect(dev_name, echo_name);
+    if (echo_fd < 0) {
+        fprintf(stderr, "Failed to connect to service\n");
+        return echo_fd;
+    }
 
-	for (i = 0; i < repeat; i++) {
+    for (i = 0; i < repeat; i++) {
+        msg_len = msgsz;
+        if (opt_variable && msgsz) {
+            msg_len = rand() % msgsz;
+        }
 
-		msg_len = msgsz;
-		if (opt_variable && msgsz) {
-			msg_len = rand() % msgsz;
-		}
+        iovs[0].iov_len = msg_len / 3;
+        iovs[1].iov_len = msg_len - iovs[0].iov_len;
 
-		iovs[0].iov_len = msg_len / 3;
-		iovs[1].iov_len = msg_len - iovs[0].iov_len;
+        memset(tx0_buf, i + 1, iovs[0].iov_len);
+        memset(tx1_buf, i + 2, iovs[1].iov_len);
+        memset(rx_buf, i + 3, sizeof(rx_buf));
 
-		memset(tx0_buf, i + 1, iovs[0].iov_len);
-		memset(tx1_buf, i + 2, iovs[1].iov_len);
-		memset(rx_buf,  i + 3, sizeof(rx_buf));
+        rc = writev(echo_fd, iovs, 2);
+        if (rc < 0) {
+            perror("writev_test: writev");
+            break;
+        }
 
-		rc = writev(echo_fd, iovs, 2);
-		if (rc < 0) {
-			perror("writev_test: writev");
-			break;
-		}
+        if ((size_t)rc != msg_len) {
+            fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "writev",
+                    (size_t)rc, msg_len);
+            break;
+        }
 
-		if ((size_t)rc != msg_len) {
-			fprintf(stderr,
-				"%s: %s: data size mismatch (%zd vs. %zd)\n",
-				__func__, "writev", (size_t)rc, msg_len);
-			break;
-		}
+        rc = read(echo_fd, rx_buf, sizeof(rx_buf));
+        if (rc < 0) {
+            perror("writev_test: read");
+            break;
+        }
 
-		rc = read(echo_fd, rx_buf, sizeof(rx_buf));
-		if (rc < 0) {
-			perror("writev_test: read");
-			break;
-		}
+        if ((size_t)rc != msg_len) {
+            fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "read",
+                    (size_t)rc, msg_len);
+            break;
+        }
 
-		if ((size_t)rc != msg_len) {
-			fprintf(stderr,
-				"%s: %s: data size mismatch (%zd vs. %zd)\n",
-				__func__, "read", (size_t)rc, msg_len);
-			break;
-		}
+        if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
+            fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+            break;
+        }
 
-		if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
-			fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
-			break;
-		}
+        if (memcmp(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+            fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+            break;
+        }
+    }
 
-		if (memcmp(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
-			fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
-			break;
-		}
-	}
+    tipc_close(echo_fd);
 
-	tipc_close(echo_fd);
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	if (!opt_silent) {
-		printf("%s: done\n",__func__);
-	}
-
-	return 0;
+    return 0;
 }
 
 static int readv_test(uint repeat, uint msgsz, bool var)
 {
-	uint i;
-	ssize_t rc;
-	size_t  msg_len;
-	int  echo_fd = -1;
-	char tx_buf [msgsz];
-	char rx0_buf[msgsz];
-	char rx1_buf[msgsz];
-	struct iovec iovs[2]= {{rx0_buf, 0}, {rx1_buf, 0}};
+    uint i;
+    ssize_t rc;
+    size_t msg_len;
+    int echo_fd = -1;
+    char tx_buf[msgsz];
+    char rx0_buf[msgsz];
+    char rx1_buf[msgsz];
+    struct iovec iovs[2] = {{rx0_buf, 0}, {rx1_buf, 0}};
 
-	if (!opt_silent) {
-		printf("%s: repeat %u: msgsz %u: variable %s\n",
-			__func__, repeat, msgsz, var ? "true" : "false");
-	}
+    if (!opt_silent) {
+        printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
+               var ? "true" : "false");
+    }
 
-	echo_fd = tipc_connect(dev_name, echo_name);
-	if (echo_fd < 0) {
-		fprintf(stderr, "Failed to connect to service\n");
-		return echo_fd;
-	}
+    echo_fd = tipc_connect(dev_name, echo_name);
+    if (echo_fd < 0) {
+        fprintf(stderr, "Failed to connect to service\n");
+        return echo_fd;
+    }
 
-	for (i = 0; i < repeat; i++) {
+    for (i = 0; i < repeat; i++) {
+        msg_len = msgsz;
+        if (opt_variable && msgsz) {
+            msg_len = rand() % msgsz;
+        }
 
-		msg_len = msgsz;
-		if (opt_variable && msgsz) {
-			msg_len = rand() % msgsz;
-		}
+        iovs[0].iov_len = msg_len / 3;
+        iovs[1].iov_len = msg_len - iovs[0].iov_len;
 
-		iovs[0].iov_len = msg_len / 3;
-		iovs[1].iov_len = msg_len - iovs[0].iov_len;
+        memset(tx_buf, i + 1, sizeof(tx_buf));
+        memset(rx0_buf, i + 2, iovs[0].iov_len);
+        memset(rx1_buf, i + 3, iovs[1].iov_len);
 
-		memset(tx_buf,  i + 1, sizeof(tx_buf));
-		memset(rx0_buf, i + 2, iovs[0].iov_len);
-		memset(rx1_buf, i + 3, iovs[1].iov_len);
+        rc = write(echo_fd, tx_buf, msg_len);
+        if (rc < 0) {
+            perror("readv_test: write");
+            break;
+        }
 
-		rc = write(echo_fd, tx_buf, msg_len);
-		if (rc < 0) {
-			perror("readv_test: write");
-			break;
-		}
+        if ((size_t)rc != msg_len) {
+            fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "write",
+                    (size_t)rc, msg_len);
+            break;
+        }
 
-		if ((size_t)rc != msg_len) {
-			fprintf(stderr,
-				"%s: %s: data size mismatch (%zd vs. %zd)\n",
-				__func__, "write", (size_t)rc, msg_len);
-			break;
-		}
+        rc = readv(echo_fd, iovs, 2);
+        if (rc < 0) {
+            perror("readv_test: readv");
+            break;
+        }
 
-		rc = readv(echo_fd, iovs, 2);
-		if (rc < 0) {
-			perror("readv_test: readv");
-			break;
-		}
+        if ((size_t)rc != msg_len) {
+            fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "write",
+                    (size_t)rc, msg_len);
+            break;
+        }
 
-		if ((size_t)rc != msg_len) {
-			fprintf(stderr,
-				"%s: %s: data size mismatch (%zd vs. %zd)\n",
-				__func__, "write", (size_t)rc, msg_len);
-			break;
-		}
+        if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
+            fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+            break;
+        }
 
-		if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
-			fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
-			break;
-		}
+        if (memcmp(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+            fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+            break;
+        }
+    }
 
-		if (memcmp(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
-			fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
-			break;
-		}
-	}
+    tipc_close(echo_fd);
 
-	tipc_close(echo_fd);
+    if (!opt_silent) {
+        printf("%s: done\n", __func__);
+    }
 
-	if (!opt_silent) {
-		printf("%s: done\n",__func__);
-	}
-
-	return 0;
+    return 0;
 }
 
 static int send_fd_test(void) {
@@ -964,51 +944,51 @@
 
 int main(int argc, char **argv)
 {
-	int rc = 0;
+    int rc = 0;
 
-	if (argc <= 1) {
-		print_usage_and_exit(argv[0], EXIT_FAILURE, false);
-	}
+    if (argc <= 1) {
+        print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+    }
 
-	parse_options(argc, argv);
+    parse_options(argc, argv);
 
-	if (!dev_name) {
-		dev_name = TIPC_DEFAULT_DEVNAME;
-	}
+    if (!dev_name) {
+        dev_name = TIPC_DEFAULT_DEVNAME;
+    }
 
-	if (!test_name) {
-		fprintf(stderr, "need a Test to run\n");
-		print_usage_and_exit(argv[0], EXIT_FAILURE, true);
-	}
+    if (!test_name) {
+        fprintf(stderr, "need a Test to run\n");
+        print_usage_and_exit(argv[0], EXIT_FAILURE, true);
+    }
 
-	if (strcmp(test_name, "connect") == 0) {
-		rc = connect_test(opt_repeat);
-	} else if (strcmp(test_name, "connect_foo") == 0) {
-		rc = connect_foo(opt_repeat);
-	} else if (strcmp(test_name, "burst_write") == 0) {
-		rc = burst_write_test(opt_repeat, opt_msgburst, opt_msgsize, opt_variable);
-	} else if (strcmp(test_name, "select") == 0) {
-		rc = select_test(opt_repeat, opt_msgburst,  opt_msgsize);
-	} else if (strcmp(test_name, "blocked_read") == 0) {
-		rc = blocked_read_test(opt_repeat);
-	} else if (strcmp(test_name, "closer1") == 0) {
-		rc = closer1_test(opt_repeat);
-	} else if (strcmp(test_name, "closer2") == 0) {
-		rc = closer2_test(opt_repeat);
-	} else if (strcmp(test_name, "closer3") == 0) {
-		rc = closer3_test(opt_repeat);
-	} else if (strcmp(test_name, "echo") == 0) {
-		rc = echo_test(opt_repeat, opt_msgsize, opt_variable);
-	} else if(strcmp(test_name, "ta2ta-ipc") == 0) {
-		rc = ta2ta_ipc_test();
-	} else if (strcmp(test_name, "dev-uuid") == 0) {
-		rc = dev_uuid_test();
-	} else if (strcmp(test_name, "ta-access") == 0) {
-		rc = ta_access_test();
-	} else if (strcmp(test_name, "writev") == 0) {
-		rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
-	} else if (strcmp(test_name, "readv") == 0) {
-		rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
+    if (strcmp(test_name, "connect") == 0) {
+        rc = connect_test(opt_repeat);
+    } else if (strcmp(test_name, "connect_foo") == 0) {
+        rc = connect_foo(opt_repeat);
+    } else if (strcmp(test_name, "burst_write") == 0) {
+        rc = burst_write_test(opt_repeat, opt_msgburst, opt_msgsize, opt_variable);
+    } else if (strcmp(test_name, "select") == 0) {
+        rc = select_test(opt_repeat, opt_msgburst, opt_msgsize);
+    } else if (strcmp(test_name, "blocked_read") == 0) {
+        rc = blocked_read_test(opt_repeat);
+    } else if (strcmp(test_name, "closer1") == 0) {
+        rc = closer1_test(opt_repeat);
+    } else if (strcmp(test_name, "closer2") == 0) {
+        rc = closer2_test(opt_repeat);
+    } else if (strcmp(test_name, "closer3") == 0) {
+        rc = closer3_test(opt_repeat);
+    } else if (strcmp(test_name, "echo") == 0) {
+        rc = echo_test(opt_repeat, opt_msgsize, opt_variable);
+    } else if (strcmp(test_name, "ta2ta-ipc") == 0) {
+        rc = ta2ta_ipc_test();
+    } else if (strcmp(test_name, "dev-uuid") == 0) {
+        rc = dev_uuid_test();
+    } else if (strcmp(test_name, "ta-access") == 0) {
+        rc = ta_access_test();
+    } else if (strcmp(test_name, "writev") == 0) {
+        rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
+    } else if (strcmp(test_name, "readv") == 0) {
+        rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
     } else if (strcmp(test_name, "send-fd") == 0) {
         rc = send_fd_test();
     } else {
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index b59fb67..f059935 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -16,7 +16,10 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
 #include <scsi/sg.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -55,6 +58,17 @@
 #define MMC_BLOCK_SIZE 512
 
 /*
+ * Number of retry attempts when an RPMB authenticated write triggers a UNIT
+ * ATTENTION
+ */
+#define UFS_RPMB_WRITE_RETRY_COUNT 1
+/*
+ * Number of retry attempts when an RPMB read operation triggers a UNIT
+ * ATTENTION
+ */
+#define UFS_RPMB_READ_RETRY_COUNT 3
+
+/*
  * There should be no timeout for security protocol ioctl call, so we choose a
  * large number for timeout.
  * 20000 millisecs == 20 seconds
@@ -104,21 +118,62 @@
 
 static const char* UFS_WAKE_LOCK_NAME = "ufs_seq_wakelock";
 
-#ifdef RPMB_DEBUG
-
-static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
+/**
+ * log_buf - Log a byte buffer to the android log.
+ * @priority: One of ANDROID_LOG_* priority levels from android_LogPriority in
+ *            android/log.h
+ * @prefix:   A null-terminated string that identifies this buffer. Must be less
+ *            than 128 bytes.
+ * @buf:      Buffer to dump.
+ * @size:     Length of @buf in bytes.
+ */
+#define LOG_BUF_SIZE 256
+static int log_buf(int priority, const char* prefix, const uint8_t* buf, size_t size) {
+    int rc;
     size_t i;
+    char line[LOG_BUF_SIZE] = {0};
+    char* cur = line;
 
-    printf("%s @%p [%zu]", prefix, buf, size);
-    for (i = 0; i < size; i++) {
-        if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), "");
-        printf(" %02x", buf[i]);
+    rc = snprintf(line, LOG_BUF_SIZE, "%s @%p [%zu]", prefix, buf, size);
+    if (rc < 0 || rc >= LOG_BUF_SIZE) {
+        goto err;
     }
-    printf("\n");
-    fflush(stdout);
-}
+    cur += rc;
+    for (i = 0; i < size; i++) {
+        if (i % 32 == 0) {
+            /*
+             * Flush the line out to the log after we have printed 32 bytes
+             * (also flushes the header line on the first iteration and sets up
+             * for printing the buffer itself)
+             */
+            LOG_PRI(priority, LOG_TAG, "%s", line);
+            memset(line, 0, LOG_BUF_SIZE);
+            cur = line;
+            /* Shift output over by the length of the prefix */
+            rc = snprintf(line, LOG_BUF_SIZE, "%*s", (int)strlen(prefix), "");
+            if (rc < 0 || rc >= LOG_BUF_SIZE) {
+                goto err;
+            }
+            cur += rc;
+        }
+        rc = snprintf(cur, LOG_BUF_SIZE - (cur - line), "%02x ", buf[i]);
+        if (rc < 0 || rc >= LOG_BUF_SIZE - (cur - line)) {
+            goto err;
+        }
+        cur += rc;
+    }
+    LOG_PRI(priority, LOG_TAG, "%s", line);
 
-#endif
+    return 0;
+
+err:
+    if (rc < 0) {
+        return rc;
+    } else {
+        ALOGE("log_buf prefix was too long");
+        return -1;
+    }
+}
 
 static void set_sg_io_hdr(sg_io_hdr_t* io_hdrp, int dxfer_direction, unsigned char cmd_len,
                           unsigned char mx_sb_len, unsigned int dxfer_len, void* dxferp,
@@ -135,6 +190,137 @@
     io_hdrp->timeout = TIMEOUT;
 }
 
+/**
+ * enum scsi_result - Results of checking the SCSI status and sense buffer
+ *
+ * @SCSI_RES_OK:    SCSI status and sense are good
+ * @SCSI_RES_ERR:   SCSI status or sense contain an unhandled error
+ * @SCSI_RES_RETRY: SCSI sense buffer contains a status that indicates that the
+ *                  command should be retried
+ */
+enum scsi_result {
+    SCSI_RES_OK = 0,
+    SCSI_RES_ERR,
+    SCSI_RES_RETRY,
+};
+
+static enum scsi_result check_scsi_sense(const uint8_t* sense_buf, size_t len) {
+    uint8_t response_code = 0;
+    uint8_t sense_key = 0;
+    uint8_t additional_sense_code = 0;
+    uint8_t additional_sense_code_qualifier = 0;
+    uint8_t additional_length = 0;
+
+    if (!sense_buf || len == 0) {
+        ALOGE("Invalid SCSI sense buffer, length: %zu\n", len);
+        return SCSI_RES_ERR;
+    }
+
+    response_code = 0x7f & sense_buf[0];
+
+    if (response_code < 0x70 || response_code > 0x73) {
+        ALOGE("Invalid SCSI sense response code: %hhu\n", response_code);
+        return SCSI_RES_ERR;
+    }
+
+    if (response_code >= 0x72) {
+        /* descriptor format, SPC-6 4.4.2 */
+        if (len > 1) {
+            sense_key = 0xf & sense_buf[1];
+        }
+        if (len > 2) {
+            additional_sense_code = sense_buf[2];
+        }
+        if (len > 3) {
+            additional_sense_code_qualifier = sense_buf[3];
+        }
+        if (len > 7) {
+            additional_length = sense_buf[7];
+        }
+    } else {
+        /* fixed format, SPC-6 4.4.3 */
+        if (len > 2) {
+            sense_key = 0xf & sense_buf[2];
+        }
+        if (len > 7) {
+            additional_length = sense_buf[7];
+        }
+        if (len > 12) {
+            additional_sense_code = sense_buf[12];
+        }
+        if (len > 13) {
+            additional_sense_code_qualifier = sense_buf[13];
+        }
+    }
+
+    switch (sense_key) {
+        case NO_SENSE:
+        case 0x0f: /* COMPLETED, not present in kernel headers */
+            ALOGD("SCSI success with sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
+                  additional_sense_code, additional_sense_code_qualifier);
+            return SCSI_RES_OK;
+        case UNIT_ATTENTION:
+            ALOGD("UNIT ATTENTION with sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
+                  additional_sense_code, additional_sense_code_qualifier);
+            if (additional_sense_code == 0x29) {
+                /* POWER ON or RESET condition */
+                return SCSI_RES_RETRY;
+            }
+
+            /* treat this UNIT ATTENTION as an error if we don't recognize it */
+            break;
+    }
+
+    ALOGE("Unexpected SCSI sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
+          additional_sense_code, additional_sense_code_qualifier);
+    log_buf(ANDROID_LOG_ERROR, "sense buffer: ", sense_buf, len);
+    return SCSI_RES_ERR;
+}
+
+static enum scsi_result check_sg_io_hdr(const sg_io_hdr_t* io_hdrp) {
+    if (io_hdrp->status == 0 && io_hdrp->host_status == 0 && io_hdrp->driver_status == 0) {
+        return SCSI_RES_OK;
+    }
+
+    if (io_hdrp->status & 0x01) {
+        ALOGE("SG_IO received unknown status, LSB is set: %hhu", io_hdrp->status);
+    }
+
+    if (io_hdrp->masked_status != GOOD && io_hdrp->sb_len_wr > 0) {
+        enum scsi_result scsi_res = check_scsi_sense(io_hdrp->sbp, io_hdrp->sb_len_wr);
+        if (scsi_res == SCSI_RES_RETRY) {
+            return SCSI_RES_RETRY;
+        } else if (scsi_res != SCSI_RES_OK) {
+            ALOGE("Unexpected SCSI sense. masked_status: %hhu, host_status: %hu, driver_status: "
+                  "%hu\n",
+                  io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);
+            return scsi_res;
+        }
+    }
+
+    switch (io_hdrp->masked_status) {
+        case GOOD:
+            break;
+        case CHECK_CONDITION:
+            /* handled by check_sg_sense above */
+            break;
+        default:
+            ALOGE("SG_IO failed with masked_status: %hhu, host_status: %hu, driver_status: %hu\n",
+                  io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);
+            return SCSI_RES_ERR;
+    }
+
+    if (io_hdrp->host_status != 0) {
+        ALOGE("SG_IO failed with host_status: %hu, driver_status: %hu\n", io_hdrp->host_status,
+              io_hdrp->driver_status);
+    }
+
+    if (io_hdrp->resid != 0) {
+        ALOGE("SG_IO resid was non-zero: %d\n", io_hdrp->resid);
+    }
+    return SCSI_RES_ERR;
+}
+
 static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
     struct {
         struct mmc_ioc_multi_cmd multi;
@@ -153,7 +339,7 @@
         mmc_ioc_cmd_set_data((*cmd), write_buf);
 #ifdef RPMB_DEBUG
         ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
-        print_buf("request: ", write_buf, req->reliable_write_size);
+        log_buf(ANDROID_LOG_INFO, "request: ", write_buf, req->reliable_write_size);
 #endif
         write_buf += req->reliable_write_size;
         mmc.multi.num_of_cmds++;
@@ -169,7 +355,7 @@
         mmc_ioc_cmd_set_data((*cmd), write_buf);
 #ifdef RPMB_DEBUG
         ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
-        print_buf("request: ", write_buf, req->write_size);
+        log_buf(ANDROID_LOG_INFO, "request: ", write_buf, req->write_size);
 #endif
         write_buf += req->write_size;
         mmc.multi.num_of_cmds++;
@@ -207,6 +393,8 @@
     struct sec_proto_cdb out_cdb = {0xB5, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00};
     unsigned char sense_buffer[32];
 
+    bool is_request_write = req->reliable_write_size > 0;
+
     wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME);
     if (wl_rc < 0) {
         ALOGE("%s: failed to acquire wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
@@ -215,30 +403,44 @@
 
     if (req->reliable_write_size) {
         /* Prepare SECURITY PROTOCOL OUT command. */
-        out_cdb.length = __builtin_bswap32(req->reliable_write_size);
         sg_io_hdr_t io_hdr;
-        set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
-                      req->reliable_write_size, (void*)write_buf, (unsigned char*)&out_cdb,
-                      sense_buffer);
-        rc = ioctl(sg_fd, SG_IO, &io_hdr);
-        if (rc < 0) {
-            ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
-            goto err_op;
-        }
+        int retry_count = UFS_RPMB_WRITE_RETRY_COUNT;
+        do {
+            out_cdb.length = __builtin_bswap32(req->reliable_write_size);
+            set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
+                          req->reliable_write_size, (void*)write_buf, (unsigned char*)&out_cdb,
+                          sense_buffer);
+            rc = ioctl(sg_fd, SG_IO, &io_hdr);
+            if (rc < 0) {
+                ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+                goto err_op;
+            }
+        } while (check_sg_io_hdr(&io_hdr) == SCSI_RES_RETRY && retry_count-- > 0);
         write_buf += req->reliable_write_size;
     }
 
     if (req->write_size) {
         /* Prepare SECURITY PROTOCOL OUT command. */
-        out_cdb.length = __builtin_bswap32(req->write_size);
         sg_io_hdr_t io_hdr;
-        set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
-                      req->write_size, (void*)write_buf, (unsigned char*)&out_cdb, sense_buffer);
-        rc = ioctl(sg_fd, SG_IO, &io_hdr);
-        if (rc < 0) {
-            ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
-            goto err_op;
-        }
+        /*
+         * We don't retry write response request messages (is_request_write ==
+         * true) because a unit attention condition between the write and
+         * requesting a response means that the device was reset and we can't
+         * get a response to our original write. We can only retry this SG_IO
+         * call when it is the first call in our sequence.
+         */
+        int retry_count = is_request_write ? 0 : UFS_RPMB_READ_RETRY_COUNT;
+        do {
+            out_cdb.length = __builtin_bswap32(req->write_size);
+            set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
+                          req->write_size, (void*)write_buf, (unsigned char*)&out_cdb,
+                          sense_buffer);
+            rc = ioctl(sg_fd, SG_IO, &io_hdr);
+            if (rc < 0) {
+                ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+                goto err_op;
+            }
+        } while (check_sg_io_hdr(&io_hdr) == SCSI_RES_RETRY && retry_count-- > 0);
         write_buf += req->write_size;
     }
 
@@ -252,6 +454,7 @@
         if (rc < 0) {
             ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
         }
+        check_sg_io_hdr(&io_hdr);
     }
 
 err_op:
@@ -353,7 +556,7 @@
         goto err_response;
     }
 #ifdef RPMB_DEBUG
-    if (req->read_size) print_buf("response: ", read_buf, req->read_size);
+    if (req->read_size) log_buf(ANDROID_LOG_INFO, "response: ", read_buf, req->read_size);
 #endif
 
     if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
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 6cd381f..21ea7ae 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -23,7 +23,7 @@
 # HAL loading of gatekeeper.trusty.
 
 PRODUCT_PACKAGES += \
-	android.hardware.keymaster@4.0-service.trusty \
+	android.hardware.security.keymint-service.trusty \
 	android.hardware.gatekeeper@1.0-service.trusty \
 	trusty_apploader